1. 数据类型和变量
- 对于很大的数,python允许再数字中间以_分隔,比如1000000000和1_000_000_000是等价的
- 如果字符串内部有很多字符都要转义,可以使用r""或r’'取消字符串内部的转义
- 如果字符串内部有很多换行,可以使用多行字符串```,比如
- python中可以对同一变量反复赋值,而且可以是不同类型的数据
2. 编码和输出
2.1 编码
-
ASCII编码1个字节,Unicode编码通常是2个字节(部分4字节),ASCII转Unicode直接前面补0
-
UTF-8编码是针对Unicode的一种可变长度字符编码,UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。
-
在计算机内存中,统一使用Unicode编码,字符串是str类型,以字符为单位。当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码,是以字节为单位的bytes类型,bytes类型用b’'或b""表示
-
str类型通过
encode()
方法转换为指定编码(eg,ascii,utf-8)的bytes类型,bytes类型通过decode()
方法转换为指定编码的str类型 -
由于Python源代码也是一个文本文件,需要将该源码保存为UTF-8编码,比如源码开头有:
#!/usr/bin/env python3 # -*- coding: utf-8 -*-
第一行注释是为了告诉Linux/OS X系统,这是一个Python可执行程序,Windows系统会忽略这个注释;
第二行注释是为了告诉Python解释器,按照UTF-8编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。
2.2 格式化输出
-
%
用来格式化字符串,比如print('I am %s, I am %d years old, I am %.2f cm in height.' % ('Zhang', 22, 180.5)) # %s可以将任意类型转换为字符串输出 print("I am %s, I am %s years old" % ('Zhang', 22))
-
使用字符串的
format()
方法格式化输出print('I am {0}, I am {1:.2f}cm in height'.format('Zhang', 180.512))
-
使用
f-string
(f开头的字符串),会进行相应的变量替换a, b = 1, 2.125 print(f'a is {a}, b is {b:.2f}')
3. 函数
3.1 函数
-
函数名其实就是一个指向函数对象的引用,可以把函数名赋给一个变量,相当于取了别名
a = abs print(a(-1))
-
函数的返回值其实是一个tuple,按位置取出
3.2 函数参数
-
必选参数(也称位置参数)在前,默认参数在后
- 如果在参数列表中加入斜杠
/
,则/
前面的变量只能通过位置参数方式传参,而且不能有默认值
- 如果在参数列表中加入斜杠
-
默认参数必须是不可变对象(比如None,数字,字符串,bool)
python可变类型和不可变类型的区别 python的可变和不可变数据类型
-
默认参数的值在函数定义的时候已经确定
-
默认参数如果没有赋值,那么默认参数重新指向默认值
- 如果默认参数是不可变类型,函数中间改变参数的值时,相当于新建了一个对象,内存地址发生了改变
- 但如果默认参数是可变类型,函数中间改变参数的值时,不会新建对象,内存地址不改变。因此下次使用该函数时默认参数的值会不再是初始值,eg:
def func(lst = []): # 错误写法,应该携程lst = None,后面在函数内部进行if判断,然后初始化 lst.append("end") print(lst) func() # ['end'] func() # ['end', 'end'] func() # ['end', 'end', 'end']
-
还要注意Python函数的参数传递是共享传参,即将实参的引用传递给函数对应的形参,因此对于不可变类型的数据经过传参在函数内部改变后,函数外的实参也会改变
-
-
可变参数:用于接受多个(或0个)参数,且参数个数不确定,eg:
def add(*numbers): # 在函数内部,numbers是一个tuple,所以无法修改 # add numbers # 用法1: add(1,2,3) # 用法2: lst = [1, 2, 3] add(*lst)
- 同时可变参数可以收集未匹配的位置参数,元组解包的过程中会将每一个元素依次放入到位置参数
*
出现在函数定义时,表示可变参数,*
出现在函数调用时,表示解包功能
-
关键字参数:接受多个(或0个)含参数名的参数,eg:
def person(name, age, **kw): # 函数内部对kw(dict类型)的改动不会影响到函数外的实参,因为传入的是拷贝 print('name', name, 'age', age, 'other', kw) extra = {'city': 'Beijing'} person('zhang', 22, **extra)
-
命名关键字参数:用于限制关键字参数的名字,
*
后面的参数被视为命名关键字参数-
如果有可变参数,后面就不需要
*
了 -
命名关键字参数可以有默认值
def person(name, *, age=22, city): pass person("zhang", city="beijing")
-
-
参数组合:必选参数–>默认参数–>可变参数–>命名关键字参数–>关键字参数
4. 数据结构及算法
4.1 数据结构
- tuple:当tuple中只有一个元素时,需要在该元素加一个逗号防止歧义(输出也有这个逗号),eg:
tup = (1,)
- 切片:可以对list,tuple, string切片操作
4.2 列表生成式
[表达式 for循环 过滤条件]
lst = [x if x % 5 == 0 else -x for x in range(1, 31) if x % 2 != 0 ]
# for x in range(1, 31)是for循环部分,if x % 2 != 0是过滤条件(不能有else语句)
# x if x % 5 == 0 else -x是表达式部分,必须有else语句(如果有if语句)
4.3 生成器
列表元素可以通过某种算法推算出来,节省内存
-
使用方法1:类似列表生成式
gen = (x*x for x in range(10)) # 使用next(gen)获得generator的下一个返回值,generator对象是可迭代对象 for x in gen: print(x)
-
使用方法2:
yield
关键字def Fibonacci(max): n, a, b = 0, 0, 1 while n < max: yield b # pring(b) a, b, n = b, a+b, n+1 for x in Fibonacci(6): print(x)
generator函数在每次调用
next()
的时候执行,遇到yield
关键字就返回,下次执行时从上次返回的yield关键字处继续执行,generator对象可迭代 -
技巧:用生成器返回一个无限序列,比如返回奇数序列:
def odd_iter(): n = 1 while True: n += 2 yield n
4.4 迭代器
-
可迭代对象
Iterable
:容器类(list,tuple,dict,set,str)和generator,凡是可迭代对象都可以迭代,enumerate
函数将list转换成index-element pair -
迭代器
Iterator
:generator是Iterator(计算是惰性的),使用iter()
将Iterable
转换为Iterator
from collections.abs import Iterable, Iterator isinstance("abc", Iterable)
5. 函数式编程
5.1 高阶函数
函数(名)作为参数传入另一个函数
-
map(f, Iterable)
->Iterator
:将函数f作用在Iterable中每个元素(将运算规则抽象了,增加了可读性) -
reduce(f, Iterable)
:reduce把结果继续和下一个元素进行累积运算,返回值是f的返回值类型需要先导入# 具体过程:reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4) from functools import reduce
-
filter(f, Iterable)
->Iterator
:将函数f作用在Iterable中每个元素,如果返回值是True就保留,否则丢弃 -
sorted(Iterable, key=func, reversed=False)
5.2 匿名函数
lambda 参数: 表达式
,可以将匿名函数赋给一个变量
5.3 闭包
-
外部函数返回内部函数时,外部函数的变量和参数仍然被返回的内部函数所引用
def lazy_sum(*args): local_var = 0 def sum(): # calc result, use local_var and *args return result return sum
-
如果内部函数修改外部函数的变量,直接修改是不行的(因为虽然内部函数可以使用外部函数的变量,但是没有初始化),需要使用nonloacl关键字表示该变量在外部函数中已经初始化了
def lazy_sum(*args): local_var = 0 def sum(): nonlocal local_var # 表示这个变量是外部函数的变量,且已经初始化为0 tmp = 0 # 这是内部函数的局部变量,每次调用sum时重新初始化 # calc return result return sum
5.4 装饰器Decorator
在代码运行期间动态增加功能的方法,本质上是一个返回函数的高阶函数
-
写法一:不带参数
def log(func): @functools.wraps(func) # 在运行add函数创建函数对象时,eg:a=add,a的__name__属性不加这句变成了wrapper,对于某些依赖函数签名的代码会出错,加了之后a的__name__属性变成了add def wrapper(*args, **kw): print(func.__name__) return func(*args, **kw) return wrapper @log def add(x, y): return x+y add(1,2) # 如果不加@修饰器使用高阶函数的写法:log(add)(1,2)
-
写法二:带参数
def log(text): def decorator(func): @functools.wraps(func) def wrapper(*args, **kw): print(text, func.__name__) func(*args, **kw) print('end') return wrapper return decorator @log("test") # 注意装饰器的参数不能是形参,一定要赋值 def add(x, y): return x+y add(1,2) # 如果不加@修饰器使用高阶函数的写法:log("test")(add)(1,2)
5.5 偏函数
将函数的一个参数固定(即设置默认值),返回一个新的函数,使之调用更简单
par_func = functools.partial(func, *args, **kw)
6. 面向对象编程
6.1 基础
6.1.1 类Class
相当于一种数据类型
6.1.2 访问限制
双下划线变量是private变量,不要在类外直接修改
class Student(object):
def __init__(self, name):
self.__name = name
def get_name(self):
print(self.__name)
def set_name(self, name):
self.__name = name
stu = Student("zhang")
stu.get_name()
print()
stu.__name = "li" # Python解释器将Student类中的__name变量修改成了_Student__name,此处的__name变量是一个新的变量了
stu.get_name()
print(stu.__name, stu._Student__name)
print()
stu.set_name("zhao")
stu.get_name()
print(stu.__name, stu._Student__name)
6.1.3 获取对象信息
-
isinstance()
判断一个对象是否是某个类型,或者位于该类型的父类继承链上。并且可以判断一个变量是否是某些类型中的一类,eg:isinstance([1,2,3], (list, tuple))
-
dir()
获取一个对象的所有方法和属性 -
hasattr(obj, 'x')
:判断obj对象是否有属性x -
getattr(obj, 'x', 404)
:获取obj对象的属性x,如果没有返回404 -
setattr(obj, 'x', 1)
:设置obj兑现的属性x
6.1.4 属性
相同名称的实例属性将屏蔽类属性
-
实例属性:
class Student: def __init__(self): self.name = 'zhang' stu = Student() print(stu.name) # 使用实例名.实例属性来访问实例属性
-
类属性:
class Student: name = 'zhang' print(Student.name) # 使用类名.类属性来访问类属性
6.1.5 多态
子类重写父类的方法
- 比如函数func的参数是Animals类型,继承自Animals类型的Dog、Cat类型(对拓展开放)的实例同样可以传入func函数(对修改封闭)
- 静态语言(eg:Java)如果函数参数是Animals类型,就必须传入Animals类型或是Animals类型的子类;但是动态语言(eg:Python)如果函数参数是Animals类型,只需要保证传入的对象中有函数中调用的方法即可(不需要必须是Animals类型,即鸭子类型)
6.2 @property
将getter/setter
方法简化为对属性的赋值和访问
-
对实例属性使用getter和setter:
class Student: def get_score(self): return self._score def set_score(self, score): ''' type check ''' self._score = score stu = Student() stu.set_score(60) print(stu.get_score())
-
使用
@property
:class Student: @property # 表明将score(原来的getter方法)当作类的属性 def score(self): return self._score # getter、setter操作的变量 @score.setter # @property创建了另一个装饰器@property.setter,可以将setter方法变成属性赋值,如果只设置@property而不设置@property.setter,则该属性被认为是只读属性 def score(self, score): ''' type check ''' self._score = score stu = Student() stu.score = 60 # 原来的setter方法 print(stu.score) # 原来的getter方法
6.3 多重继承
- MixIn设计模式:以一个父类为主类,其他父类视为增加的功能
6.4 定制类
-
__str__
:重新定义print类实例的输出class Student: def __str__(self): return 'Student object' print(Student())
-
__iter__
:返回一个用于for循环的迭代对象,该迭代对象调用__next__
方法获取下一个值class Fibonacci(object): def __init__(self): self.a, self.b = 0, 1 def __iter__(self): return self # 实例本身就是迭代对象,故返回自己 def __next__(self): self.a, self.b = self.b, self.a+self.b return self.a for n in Fibonacci(): if n > 100: break print(n)
-
__getitem__
:按下标(或者key)取值,同样还有__setitem__
、__delitem__
class Fibonacci(object): def __getitem__(self, n): # 如果想实现切片操作,需要进一步完善 a, b, = 1, 1 for x in range(n): a, b = b, a+b return a
-
__call__
:对类的实例进行调用class Student(object): def __call__(self, txt): print("test: %s" % txt) s = Student() s("txt") # 对实例进行调用 callable(s) # 判断一个对象能否被调用
-
__slots__
:Python可以给实例或类绑定方法或属性,使用__slots__可以限制绑定的实例的属性和方法
6.5 枚举类
将一组常量定义在一个类中,且该类不可变
from enum import Enum
Mon = Enum('Month', ("Jan", "Feb", "Mar")) # Month类型的枚举类
for name, member in Mon.__members__.items():
print(name, '=>', member, ',', member.value)
print(Mon(1)) # 根据value的值获取枚举常量
print(Mon.Jan) # 根据成员名称引用枚举常量
print(Mon['Jan'])
print(Mon.Jan.value) # 获取计数,从1开始
7. 调试
-
断言:可以使用
python -O test.py
关闭assertdef func(x): assert x!=0, "x is zero" return x//10
-
日志
logging
:将print替换为logging
8. 文件
8.1 文件读写
with open('tset.txt', 'r', encoding='utf-8') as f:
# f.read() # 会将文件内容一次性读取
for line in f.readlines():
print(line.strip()) # 把末尾的'\n'删掉
# 或者line=f.readline()循环读直到line==''
# 当文件写入磁盘时,操作系统不会将数据立刻写入磁盘,而是放到缓存中,f.close()才保证全部写入磁盘
8.2 JSON文件读写
json.dump(obj, f)
:python对象obj序列化到文件fjson.dumps(obj)
:将python对象obj序列化为json格式字符串json.load(f)
:将文件f反序列化为python对象json.loads()
:将json格式字符串反序列化为python对象
8.3 StringIO与BytesIO
StringIO
:在内存中读写str,BytesIO
:在内存中读写bytes,与读写文件有相同的接口
8.4 操作文件和目录
os.uname()
获取详细的系统信息os.environ
查看系统变量os.path.abspath('.')
当前目录的绝对路径os.path.join()
将多个路径合成一个os.path.split()
将路径拆分为两个部分,后一部分为最后的目录或文件名os.path.splitext()
得到路径的文件拓展名os.mkdir(), os.rmdir(), os.remove(), os.rename()
shutil.copyfile()
文件复制
9. 更多
Python Cookbook 3rd Edition Documentation
Python中文指南
Python黑魔法手册