Py_learning
由于我在学习机器学习算法的时候,希望通过Python来对相关的算法进行复现。而自己在此之前其实零零散散不成体系地接触过Python语言,也了解一些基本的东西,但是对于Python中一些语言“特性”方面的东西所知甚少,例如变量的作用域与生命周期,不同模块间的访问等等;此外我对Python风格的代码写法也并不熟悉,其实写什么感觉都是C的味道......于是写下这篇blog用来记录,进一步对相关内容的学习
模块化的Python程序
内置变量__name__
__name__是python模块当中的一个内置变量,每个模块都有。如果你选择当前模块开始执行,那么当前模块内置的__name__会被置为__main__;如果一个模块是被令一个模块import进去的,那么这个模块的__name__会被置为__模块名__,但是不会引入后缀。
模块化
通过__name__我们就可以将我整个项目文件模块化的组织起来。将一个模块作为程序的执行入口,并始终自我约束地从这个模块开始启动整个项目程序。这样做的关键在于使用如下代码:
1 | def main: |
关键点即,不要使用判断__name__以外的任何顶层代码
一些特性
Python是一种解释性语言,特点就是不需要编译,而是在运行时通过解释器逐行读取、分析和执行源代码。对应的特点之一就是交互式的编程环境(可以在命令行中输入代码,并立刻看到执行的结果)
我联想到与这种特点相对应的就是——“顶层代码”,即相关的语句不会被封装在任何函数和类当中,点击运行,便会至上而下地逐行开始执行。
所以一个关键的特性就是,使用import导入模块化后,该模块的顶层代码会立刻执行。
启示:编写规范化的工程代码时,除了判断程序执行入口,不要使用顶层代码
1 | #k_means.py |
输出结果:
this is k_means this is main
变量的作用域和生命周期
单一模块
全局变量
在同一模块当中,定义于模块层的变量(顶层代码部分),对应的是global varible
全局变量,这些变量的作用域是全局可见,生命周期是从程序开始执行开始,执行完毕结束。局部变量
定义于函数中的变量是local varible
局部变量,作用域局部可见。对于嵌套函数,外层变量对内层可见,内层对外层不可见。在Python中这种函数嵌套更加的显然。下面是一个例子:
1 | def outer_function: |
对应变量的生命周期,都是从定义自己的函数开始,到函数执行完毕结束。
另外值得一提的是,在上面这个例子当中,inner_function不能从顶层代码调用。
3. 内置变量Built-in varible
内置变量的作用域是在任何地方都可以访问,且生命周期贯穿整个程序的运行期,最开始提到的__name__就是一个很好的例子。
4. 访问规则
python对于变量遵循LEGB
的访问规则,即局部、嵌套、全局、内置。当发现了变量,即刻使用。
最后简单补充以下Python的变量定义规则,变量在“第一次赋值”时被定义。当然这意味着我们要定义一个变量必须考虑一个初始值,如果暂时没有初始值的话可以使用None
作为初始值。随后根据需要赋予想要的初始值即可。当然,变量的类型也是根据你赋予的值来确定的。
多模块
为了理解多模块情况下相关变量的作用域和生命周期,引入以下概念:
模块对象,在导入模块的时候Python会为模块创建一个对象,这个对象的生命周期由其作用域确定
全局导入,模块对象在全局作用域中导入,此时模块变量生命周期同程序一样。作用域同全局变量。
局部导入,模块对象在局部作用域中导入,此时模块变量生命周期同导入了它的函数。作用域同相应的局部变量。
模块中的顶层代码在被导入时会立刻执行,相应的对应的全局变量会即刻创建,所以对应的全局变量生命周期、作用域,同模块对象。
口语化的来说,模块被导入的时候也相当于一个变量(或者是一个类),如果是被主函数所在的模块作为全局变量导入,那么被导入模块的生命周期、作用域同全局变量,如果被作为局部变量导入,也同局部变量。相应的,被导入的时候,被导入模块中的“全局变量”也会即刻被创建,其生命周期同被导入的模块。(毫不精准的表述…)
名称冲突
在使用以下代码的时候,名称冲突时常发生。
1 | from somemodule import somename |
这类似是跳过了模块对象,直接导入了其中某个全局变量,自然就很可能与当前模块已有的全局变量、函数发生名称冲突。
常用的解决方法,也是我们使用模块化的常用方法如下:
1 | import somemodule |
列表生成式
语法如下:
1 | list_name = [formula for _ in range(start, end)] |
这种创建列表的方法成为列表生成式,formula是用于生成列表的表达式,可以是返回一些值的函数,后面的循环是列表中生成元素的次数,循环一次便会调用一次formula。
当然formula也可以直接是数学表达式,例如第二个例子展示的,用于生成1到9的平方的列表。
注意end不被包含在内。
元组
元组(Tuple)是一种内置的数据结构,属于不可变序列类型,用于存储多个元素。与列表(List)不同,元组一旦创建,其内容就不能更改(即不可变)。元组常用于存储一组相关的数据,例如函数返回多个值时,可以使用元组来打包这些值。
元组的创建
使用()
来创建一个元组,例如:
1 | # 创建一个空元组 |
元组的常见用途
- 多值返回,用于让函数返回多个值
- 作为字典的键,这是由于元组的不可变性
函数的参数以及返回值
在python中函数的参数不需要提前声明类型,同样的返回值也不需要提前进行声明。但是在大型的项目中为了便于程序的维护,以及提供静态的检查,可以使用注解符号。例如,下面这个例子。
1 | from typing import List, Tuple |
其中typing是类型注解使用的包,如果不需要使用类型进行注解可以不使用这个包。
常见的类型注解有:
- List eg: List[int]
- Tuple eg: Tuple[float,str]
- Dict eg: Dict[int,str]
- Set eg: Set[str]
还有许多可用的…用到再查吧…