avatar
阳生。
风毛丛劲节,只上尽头竿。

Py_learning

由于我在学习机器学习算法的时候,希望通过Python来对相关的算法进行复现。而自己在此之前其实零零散散不成体系地接触过Python语言,也了解一些基本的东西,但是对于Python中一些语言“特性”方面的东西所知甚少,例如变量的作用域与生命周期,不同模块间的访问等等;此外我对Python风格的代码写法也并不熟悉,其实写什么感觉都是C的味道......于是写下这篇blog用来记录,进一步对相关内容的学习

模块化的Python程序

内置变量__name__

__name__是python模块当中的一个内置变量,每个模块都有。如果你选择当前模块开始执行,那么当前模块内置的__name__会被置为__main__;如果一个模块是被令一个模块import进去的,那么这个模块的__name__会被置为__模块名__,但是不会引入后缀。

模块化

通过__name__我们就可以将我整个项目文件模块化的组织起来。将一个模块作为程序的执行入口,并始终自我约束地从这个模块开始启动整个项目程序。这样做的关键在于使用如下代码:

1
2
3
4
5
def main:
something

if __name__ == __main__:
main()

关键点即,不要使用判断__name__以外的任何顶层代码

一些特性

Python是一种解释性语言,特点就是不需要编译,而是在运行时通过解释器逐行读取、分析和执行源代码。对应的特点之一就是交互式的编程环境(可以在命令行中输入代码,并立刻看到执行的结果)

我联想到与这种特点相对应的就是——“顶层代码”,即相关的语句不会被封装在任何函数和类当中,点击运行,便会至上而下地逐行开始执行。

所以一个关键的特性就是,使用import导入模块化后,该模块的顶层代码会立刻执行。

启示:编写规范化的工程代码时,除了判断程序执行入口,不要使用顶层代码

1
2
3
4
5
6
7
8
#k_means.py
print("this is k_means")

#main.py
def main:
print("this is mainn")
if __name__ == main:
main()

输出结果:

this is k_means this is main

变量的作用域和生命周期

单一模块

  1. 全局变量
    在同一模块当中,定义于模块层的变量(顶层代码部分),对应的是global varible全局变量,这些变量的作用域是全局可见,生命周期是从程序开始执行开始,执行完毕结束。

  2. 局部变量
    定义于函数中的变量是local varible局部变量,作用域局部可见。对于嵌套函数,外层变量对内层可见,内层对外层不可见。在Python中这种函数嵌套更加的显然。下面是一个例子:

1
2
3
4
5
def outer_function:
print("this is outer")
def inner_function:
print("this is inner")
inner_function()

对应变量的生命周期,都是从定义自己的函数开始,到函数执行完毕结束。

另外值得一提的是,在上面这个例子当中,inner_function不能从顶层代码调用。
3. 内置变量
Built-in varible内置变量的作用域是在任何地方都可以访问,且生命周期贯穿整个程序的运行期,最开始提到的__name__就是一个很好的例子。
4. 访问规则
python对于变量遵循LEGB的访问规则,即局部、嵌套、全局、内置。当发现了变量,即刻使用。

最后简单补充以下Python的变量定义规则,变量在“第一次赋值”时被定义。当然这意味着我们要定义一个变量必须考虑一个初始值,如果暂时没有初始值的话可以使用None作为初始值。随后根据需要赋予想要的初始值即可。当然,变量的类型也是根据你赋予的值来确定的。

多模块

为了理解多模块情况下相关变量的作用域和生命周期,引入以下概念:

  1. 模块对象,在导入模块的时候Python会为模块创建一个对象,这个对象的生命周期由其作用域确定

  2. 全局导入,模块对象在全局作用域中导入,此时模块变量生命周期同程序一样。作用域同全局变量。

  3. 局部导入,模块对象在局部作用域中导入,此时模块变量生命周期同导入了它的函数。作用域同相应的局部变量。

  4. 模块中的顶层代码在被导入时会立刻执行,相应的对应的全局变量会即刻创建,所以对应的全局变量生命周期、作用域,同模块对象。

口语化的来说,模块被导入的时候也相当于一个变量(或者是一个类),如果是被主函数所在的模块作为全局变量导入,那么被导入模块的生命周期、作用域同全局变量,如果被作为局部变量导入,也同局部变量。相应的,被导入的时候,被导入模块中的“全局变量”也会即刻被创建,其生命周期同被导入的模块。(毫不精准的表述…)

名称冲突

在使用以下代码的时候,名称冲突时常发生。

1
from somemodule import somename

这类似是跳过了模块对象,直接导入了其中某个全局变量,自然就很可能与当前模块已有的全局变量、函数发生名称冲突。

常用的解决方法,也是我们使用模块化的常用方法如下:

1
2
3
4
import somemodule
somemodule.somename #使用模块对象名来访问相应的变量、函数

from somemodule import somename as another_name #或者是别名

列表生成式

语法如下:

1
2
list_name = [formula for _ in range(start, end)]
list_name = [x**2 for x in range(1,10)]

这种创建列表的方法成为列表生成式,formula是用于生成列表的表达式,可以是返回一些值的函数,后面的循环是列表中生成元素的次数,循环一次便会调用一次formula。

当然formula也可以直接是数学表达式,例如第二个例子展示的,用于生成1到9的平方的列表。

注意end不被包含在内。

元组

元组(Tuple)是一种内置的数据结构,属于不可变序列类型,用于存储多个元素。与列表(List)不同,元组一旦创建,其内容就不能更改(即不可变)。元组常用于存储一组相关的数据,例如函数返回多个值时,可以使用元组来打包这些值。

元组的创建

使用()来创建一个元组,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 创建一个空元组
empty_tuple = ()

# 创建一个包含多个元素的元组
example_tuple = (1, 2, 3, "hello", 4.5)

# 创建一个单元素的元组(注意逗号)
single_element_tuple = (1,)

# 也可以省略小括号,直接用逗号分隔元素
another_tuple = 1, 2, 3

# 访问元素
print(example_tuple[0]) # 输出 1
print(example_tuple[3]) # 输出 "hello"

# 获取元组的长度
print(len(example_tuple)) # 输出 5

元组的常见用途

  1. 多值返回,用于让函数返回多个值
  2. 作为字典的键,这是由于元组的不可变性

函数的参数以及返回值

在python中函数的参数不需要提前声明类型,同样的返回值也不需要提前进行声明。但是在大型的项目中为了便于程序的维护,以及提供静态的检查,可以使用注解符号。例如,下面这个例子。

1
2
3
from typing import List, Tuple

def k_means(D:List[List[float]], n:int, k:int) -> Tuple(List[List[List[float]]], Lsit[List[float]])

其中typing是类型注解使用的包,如果不需要使用类型进行注解可以不使用这个包。

常见的类型注解有:

  1. List eg: List[int]
  2. Tuple eg: Tuple[float,str]
  3. Dict eg: Dict[int,str]
  4. Set eg: Set[str]
    还有许多可用的…用到再查吧…
Site by 阳生 | Powered by Hexo | theme PreciousJoy