Python - doranbai/Note GitHub Wiki

赋值语句不是表达式

C语言的赋值语句是一种表达式,确切的说应该叫赋值表达式。=在C语言中是操作符,所以a=b是赋值表达式。而且存在返回值 a=b=5,先b=5,这个表达式的返回值为5,然后a=5 Python中的赋值语句是语句,类似print语句。不是表达式,=不是操作符,仅是一个语法分隔符。所以在Python的交互式解释器上,2+1会输出3,因为这是一个表达式,而a=1却没有任何输出,因为它是一个语句

input的输入是一个str

in是一个运算符,返回值为True or False

列表的相加是连接。如字符串的连接

序列:字符串 元组 列表 字典 等... 字符串和他们并列

三个内建函数:列表,元组和字符串,他们之间的互相转换使用三个函数,str(),tuple()和list(),这些函数传入一个序列,返回一个序列

----------str            tuble            list
str       str(string)    "".join(tuble)   "".join(list)
tuble     tuble(stirng)  tuble(tuble)     tuble(list) 
list      list(string)   list(tuble)      list(list)

*和**的打包和解包

*的作用:在函数定义中,收集所有的位置参数到一个新的元组;在函数调用中,*能够将元组或者列表解包成不同的参数 ** 的作用:在函数定义中,在函数定义中,收集关键字参数传递给一个字典;在函数调用中,**会以键/值的形式解包一个字典,使其成为独立的关键字参数

>>> a, b, *c = 0, 1, 2, 3
>>> a
0
>>> b
1
>>> c
[2, 3]

序列打包

序列解包(可迭代对象解包)

任何可迭代对象都支持解包,可迭代对象包括元组、字典、集合、字符串、生成器等实现了__next__方法的一切对象。

>>> a, b = 1, 2
>>> a
1
>>> b
2

本质上也是自动解包过程,等号右边其实是一个元组对象 (1, 2),有时候我们添加一个逗号 ,就变成了元组对象

链式赋值

其实本质上是自动解包。解包顺序 a=b=1先是a=1,之后b=1

is是个运算符

del语句

del语句在Python下用法非常丰富,del语句不仅可以用来删除普通变量,还可以用来删除序列和字典元素。它不仅会移除一个对象的引用(猜测减少引用计数),会移除那个名字本身。

>>> x = 123
>>> x 123
>>> del x
>>> x
Traceback (most recent call last):   File "", line 1, in NameError: name 'x' is not defined

推导式

条件推导式

类似于三目运算符

 z = x if x > y else y

列表推导式

[Expression for Variable in  list] 
[ 表达式  for  变量 in 列表]
[表达式 for 变量 in 列表 if 条件]
[exp1 if condition else exp2 for x in data]
new_list = [expression(i) for i in old_list if condition(i)]

列表解析式是将一个列表(实际上适用于任何可迭代对象)转换成另一个列表的工具。

函数

函数没有返回值的时候,它们就返回 None

as是一个关键字

>>> import re
>>> import re as regex
>>> 
>>> id(re) == id(regex)
True

魔法方法

带下划线的那些方法是魔法方法

基础魔法方法(较为常用)

new(cls[, ...]) 才是实例化对象调用的第一个方法,它只取下 cls 参数,并把其他参数传给 init。 __new__很少使用,但是也有它适合的场景(单例模式),尤其是当类继承自一个像元组或者字符串这样不经常改变的类型的时候。

init(self[, ...])构造方法,初始化类的时候被调用

del(self)析构方法,当实例化对象被彻底销毁时被调用(实例化对象的所有指针都被销毁时被调用)

call(self[, args...])允许一个类的实例像函数一样被调用:x(a, b) 调用 x.call(a, b)

len(self)定义当被 len() 调用时的行为__repr__(self)定义当被 repr() 调用时的行为

str(self)定义当被 str() 调用时的行为__bytes__(self)定义当被 bytes() 调用时的行为

hash(self)定义当被 hash() 调用时的行为

bool(self)定义当被 bool() 调用时的行为,应该返回 True 或 False

format(self, format_spec)定义当被 format() 调用时的行为 属性相关的方法

getattr(self, name)定义当用户试图获取一个不存在的属性时的行为

getattribute(self, name)定义当该类的属性被访问时的行为

setattr(self, name, value)定义当一个属性被设置时的行为

delattr(self, name)定义当一个属性被删除时的行为

dir(self)定义当 dir() 被调用时的行为

get(self, instance, owner)定义当描述符的值被取得时的行为

set(self, instance, value)定义当描述符的值被改变时的行为

delete(self, instance)定义当描述符的值被删除时的行为 比较操作符

lt(self, other)定义小于号的行为:x < y 调用 x.lt(y)

le(self, other)定义小于等于号的行为:x <= y 调用 x.le(y)

eq(self, other)定义等于号的行为:x == y 调用 x.eq(y)

ne(self, other)定义不等号的行为:x != y 调用 x.ne(y)

gt(self, other)定义大于号的行为:x > y 调用 x.gt(y)

ge(self, other)定义大于等于号的行为:x >= y 调用 x.ge(y)

类型转换

complex(self)定义当被 complex() 调用时的行为(需要返回恰当的值)

int(self)定义当被 int() 调用时的行为(需要返回恰当的值)

float(self)定义当被 float() 调用时的行为(需要返回恰当的值)round(self[, n])定义当被 round() 调用时的行为(需要返回恰当的值)

容器类型(一般用于操作容器类)

len(self)定义当被 len() 调用时的行为(一般返回容器类的长度)

getitem(self, key)定义获取容器中指定元素的行为,相当于 self[key]

setitem(self, key, value)定义设置容器中指定元素的行为,相当于 self[key] = value

delitem(self, key)定义删除容器中指定元素的行为,相当于 del self[key]

iter(self)定义当迭代容器中的元素的行为

reversed(self)定义当被 reversed() 调用时的行为

contains(self, item)定义当使用成员测试运算符(in 或 not in)时的行为

内置函数

type、str、dir 和其它的 Python 内置函数都归组到了 builtin (前后分别是双下划线) 这个特殊的模块中。可以认为 Python 在启动时自动执行了 from builtin import *,此语句将所有的 “内置” 函数导入该命名空间,所以在这个命名空间中可以直接使用这些内置函数。 像这样考虑的好处是,可以获取 builtin 模块信息的,并以组的形式访问所有的内置函数和属性。现在我们的 Python 有一个称为 info 的函数。自己尝试一下,略看一下结果列表。python3将__builtin__改成了builtins

查看对象的属性和方法

简洁版用 dir() 详细版用 help() 都是内置函数

import builtins
dir(builtins)
help(builtins)

module

每一个.py文件都是一个module
一个Python Module(模块),是一个文件,包含了Python对象定义和Python语句(definitions and statements)。文件名就是模块名加上后缀.py,在模块内部,模块名存储在__name__属性中,是一个string,可以直接在module中通过__name__引用到module name。 模块是一个对象,有一个__name__属性 import module

import numpy as np
相当于
import numpy
np = numpy
del numpy

from aaa import bbb 将aaa中的bbb导入当前的命名空间
from并不会节省空间,都会把资源导入,from只是控制了那些资源可见
from aaa import * 向别人直接提供默认的导出的资源,模块的开发者清楚哪些是需要导出的。需要在__init.py__中添加__all__=['有用的','资源']

模块检索路径 主py文件的路径,系统的一些路径
from . import xxx相对导入 form .. improt xxx相对导入

循环导入不会递归

package

步骤:
首先创建一个文件夹,用于存放模块(module),文件夹的名字即为包名
然后再文件夹中创建一个__init__.py文件 init.py该文件的作用就是相当于把自身整个文件夹当作一个包来管理,每当有外部import的时候,就会自动执行里面的函数。 import packagename, 会查找名为packagename的文件夹,然后查找__init__.py文件。但是不会自动导入文件夹里面的.py文件(模块)。如果需要导入,则需要再__init__.py中编写代码

第三方库的安装

pip freaze > reqirements.txt pip install -r reqirements.txt

虚拟环境的概念

实例化一个类

创建类的实例只要调用一个类,仿佛它是一个函数就行了。不像 C++ 或 Java 有一个明确的 new 操作符。

大文件读取

open函数返回的其实是一个文件迭代器
f.read(), readlines()等函数都会一次性的将文件读入到内存之中,大文件慎用,但是可以使用readline()函数逐行读取
逐行读取

with open('filename', 'r') as f:
    while True:
        line = f.readline()  # 逐行读取
        if not line:  # 到 EOF,返回空字符串,则终止循环
            break
        do_something(line)

自动读取 with open('...') as f: for line in f: process(line) #

open函数返回的那个就是文件迭代器  
for line in f其实是将文件对象f视为一个迭代器,自动的采用缓冲IO和内存管理,所以不必担心大文件。  
如果被读取的大文件里,根本就没有任何换行符,那就悲剧了。当代码执行到 for line in file 时,line 将会变成一个非常巨大的字符串对象,消耗掉非常可观的内存。  

分块读取

read(size) readlines(size)

def count_nine_v2(fname): """计算文件里包含多少个数字 '9',每次读取 8kb """ count = 0 block_size = 1024 * 8 with open(fname) as fp: while True: chunk = fp.read(block_size) # 当文件没有更多内容时,read 调用将会返回空字符串 '' if not chunk: break count += chunk.count('9') return count

生成器

假如我们在讨论的不是 Python,而是其他编程语言。那么可以说上面的代码已经很好了。但是如果你认真分析一下 count_nine_v2 函数,你会发现在循环体内部,存在着两个独立的逻辑:数据生成(read 调用与 chunk 判断) 与 数据消费。而这两个独立逻辑被耦合在了一起。

为了提升复用能力,我们可以定义一个新的 chunked_file_reader 生成器函数,由它来负责所有与“数据生成”相关的逻辑。这样 count_nine_v3 里面的主循环就只需要负责计数即可。

def chunked_file_reader(fp, block_size=1024 * 8): """生成器函数:分块读取文件内容 """ while True: chunk = fp.read(block_size) # 当文件没有更多内容时,read 调用将会返回空字符串 '' if not chunk: break yield chunk

def count_nine_v3(fname): count = 0 with open(fname) as fp: for chunk in chunked_file_reader(fp): count += chunk.count('9') return count 进行到这一步,代码似乎已经没有优化的空间了,但其实不然。iter(iterable) 是一个用来构造迭代器的内建函数,但它还有一个更少人知道的用法。当我们使用 iter(callable, sentinel) 的方式调用它时,会返回一个特殊的对象,迭代它将不断产生可调用对象 callable 的调用结果,直到结果为 setinel 时,迭代终止。

def chunked_file_reader(file, block_size=1024 * 8): """生成器函数:分块读取文件内容,使用 iter 函数 """ # 首先使用 partial(fp.read, block_size) 构造一个新的无需参数的函数 # 循环将不断返回 fp.read(block_size) 调用结果,直到其为 '' 时终止 for chunk in iter(partial(file.read, block_size), ''): yield chunk