Python
Python 虚拟环境 venv
创建虚拟环境
py -3 # python3 ?
py -3 -m venv [path]
example
py -3 -m venv venv
# 进入 \退出
venv\Scripts\activate ---> deactivate
Notes
- python 解释型编程,弱类型语言,可以重复赋值
- 浮点数存在的精度问题
- 数据类型 type()
- 这是因为
input(),
返回的数据类型是str
数据类型
a = 2
type(a)
class<'int'>
- a / b (float) | a // b (int) (取整, 整数时候, 浮点数 // 还是 浮点), 还有一种除法是 //,称为地板除
- %
10 % 3
>>> 1
10 % 3.0
>>> 1.0
Python 允许在数字中间以_分隔,因此,写成 10_000_000_000 和 10000000000 是完全一样的
布尔值可以用 and、or 和 not 运算 , not 运算是非运算,它是一个单目运算符,把 True 变成 False,False 变成 True 类似 JavaScript 的 (&& ,|| ,!)
空值是 Python 里一个特殊的值,用 None 表示。None 不能理解为 0,因为 0 是有意义的,而 None 是一个特殊的空值, 也不是空字符串
Python 的整数没有大小限制,而某些语言的整数根据其存储长度是有大小限制的,例如 Java 对 32 位整数的范围限制在-2147483648-2147483647,Python 的浮点数也没有大小限制,但是超出一定范围就直接表示为 inf(无限大)
由于计算机是美国人发明的,因此,最早只有 127 个字符被编码到计算机里,也就是大小写英文字母、数字和一些符号,这个编码表被称为 ASCII 编码,比如大写字母 A 的编码是 65,小写字母 z 的编码是 122。但是要处理中文显然一个字节是不够的,至少需要两个字节,而且还不能和 ASCII 编码冲突,所以,中国制定了 GB2312 编码,用来把中文编进去
Unicode 字符集应运而生。Unicode 把所有语言都统一到一套编码里,这样就不会再有乱码问题了
如果统一成 Unicode 编码,乱码问题从此消失了。但是,如果你写的文本基本上全部是英文的话,用 Unicode 编码比 ASCII 编码需要多一倍的存储空间,在存储和传输上就十分不划算 -> utf-8
UTF-8 编码把一个 Unicode 字符根据不同的数字大小编码成 1-6 个字节,常用的英文字母被编码成 1 个字节,汉字通常是 3 个字节,只有很生僻的字符才会被编码成 4-6 个字节
由于 Python 源代码也是一个文本文件,所以,当你的源代码中包含中文的时候,在保存源代码时,就需要务必指定保存为 UTF-8 编码。当 Python 解释器读取源代码时,为了让它按 UTF-8 编码读取,我们通常在文件开头写上这两行
# !/usr/bin/env python3 # -*- coding: utf-8 -*-
占位符,如果你不太确定应该用什么,
%s
永远起作用,它会把任何数据类型转换为字符串。有些时候,字符串里面的%
是一个普通字符怎么办?这个时候就需要转义,用%%
来表示一个%
%d 整数 %f 浮点数 %s 字符串 %x 十六进制整数 `python 'Hi, %s, you have $%d.' % ('Michael', 1000000)'Hi, Michael, you have $1000000.' #最后一种格式化字符串的方法是使用以 f 开头的字符串,称之为 f-string,它和普通字符串不同之处在于,字符串如果包含{xxx},就会以对应的变量替换 r = 2.5 s = 3.14 * r ** 2 print(f'The area of a circle with radius {r} is {s:.2f}') The area of a circle with radius 2.5 is 19.62
- :后面的.2f 指定了格式化参数(即保留两位小数)
`
列表如果要取最后一个元素,除了计算索引位置外,还可以用
-1
做索引,直接获取最后一个元素,以此类推,可以获取倒数第 2 个、倒数第 3 个list.append() list.insert(1, 'Jack') #把元素插入到指定的位置,比如索引号为1的位置 list.pop() #要删除list末尾的元素
元组(有序列表):tuple。tuple 和 list 非常类似,但是 tuple 一旦初始化就不能修改,比如同样是列出同学的名字
classmates = ('Michael', 'Bob', 'Tracy') #tuple的陷阱:当你定义一个tuple时,在定义的时候,tuple的元素就必须被确定下来 t = (1,2) >>> 1,2 t = () >>> () t = (1) >>> 1 #定义的不是tuple,是1这个数!这是因为括号()既可以表示tuple,又可以表示数学公式中的小括号,这就产生了歧义,因此,Python规定,这种情况下,按小括号进行计算,计算结果自然是1 t = (1,) >>> (1,)
条件判断 \ 循环
if <条件判断1>: <执行1> elif <条件判断2>: <执行2> elif <条件判断3>: <执行3> else: <执行4> # for >>>for name in names: # while >>>while n > 0:
dict \ set
# dist # dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度 >>>d = {'Michael': 95, 'Bob': 75, 'Tracy': 85} >>>d['Michael'] 95 # 如果key不存在,dict就会报错, 避免key不存在的错误,有两种办法, # 一是通过in判断key是否存在 >>>'Thomas' in d # 二是通过dict提供的get()方法,如果key不存在,可以返回None,或者自己指定的value >>>d.get('Thomas', -1) -1 # 要删除一个key,用pop(key)方法,对应的value也会从dict中删除 # set # set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key,重复元素在set中自动被过滤 >>>s = set([1, 1, 2, 2, 3, 3]) {1,2,3} # 通过add(key)方法可以添加元素到set中,可以重复添加,但不会有效果,通过remove(key)方法可以删除元素 #两个set可以做数学意义上的交集、并集等操作 >>> s1 = set([1, 2, 3]) >>> s2 = set([2, 3, 4]) >>> s1 & s2 {2, 3} >>> s1 | s2 {1, 2, 3, 4}
函数
空函数
# 想定义一个什么事也不做的空函数,可以用pass语句,pass可以用来作为占位符,比如现在还没想好怎么写函数的代码,就可以先放一个pass,让代码能运行起来
Notes
数据类型检查可以用内置函数
isinstance()
实现if not isinstance(x, (int, float)): raise TypeError('bad operand type')
默认参数**
def** **power**(x, n=2)
定义默认参数要牢记一点:默认参数必须指向不变对象!
可变参数**
def** **calc**(\*numbers):
在函数内部,参数numbers
接收到的是一个 tuplePython 允许你在 list 或 tuple 前面加一个
*
号,把 list 或 tuple 的元素变成可变参数传进去>>> nums = [1, 2, 3] >>> calc(*nums) 14
关键字参数
# 关键字参数有什么用?它可以扩展函数的功能。比如,在person函数里,我们保证能接收到name和age这两个参数,但是,如果调用者愿意提供更多的参数,我们也能收到。试想你正在做一个用户注册的功能,除了用户名和年龄是必填项外,其他都是可选项,利用关键字参数来定义这个函数就能满足注册的需求 def person(name, age, **kw): print('name:', name, 'age:', age, 'other:', kw) # 如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city和job作为关键字参数。这种方式定义的函数如下 def person(name, age, *, city, job): print(name, age, city, job) # 和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数 # 参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数
切片
# L[0:3]表示,从索引0开始取,直到索引3为止,但不包括索引3。即索引0,1,2,正好是3个元素 # 如果第一个索引是0,还可以省略,支持倒数切片 >>> L[-2:] ['Bob', 'Jack'] >>> L[-2:-1] ['Bob'] # 前10个数,每两个取一个 >>> L[:10:2] [0, 2, 4, 6, 8] # 所有数,每5个取一个: >>> L[::5] [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95] # 甚至什么都不写,只写[:]就可以原样复制一个list >>> L[:] [0, 1, 2, 3, ..., 99] # 字符串'xxx'也可以看成是一种list,每个元素就是一个字符。因此,字符串也可以用切片操作,只是操作结果仍是字符串 >>> 'ABCDEFG'[:3] 'ABC' >>> 'ABCDEFG'[::2] 'ACEG'
迭代 |
enumerate
函数可以把一个list
变成索引-元素对# for循环不仅可以用在list或tuple上,还可以作用在其他可迭代对象上, 字符串也是可迭代对象 # 只要是可迭代对象,无论有无下标,都可以迭代,比如dict就可以迭代 # 默认情况下,dict迭代的是key。如果要迭代value,可以用for value in d.values(),如果要同时迭代key和value,可以用for k, v in d.items() # 如何判断一个对象是可迭代对象呢?方法是通过collections.abc模块的Iterable类型判断 >>> from collections.abc import Iterable >>> isinstance('abc', Iterable) # str是否可迭代 True >>> isinstance([1,2,3], Iterable) # list是否可迭代 True >>> isinstance(123, Iterable) # 整数是否可迭代 False
列表生成式
# 要生成list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]可以用list(range(1, 11)) # **[x * x for x in range(1, 11) if x % 2 == 0] # 可以使用两层循环,可以生成全排列 >>> [m + n for m in 'ABC' for n in 'XYZ'] ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']**
generator
# 这种一边循环一边计算的机制,称为生成器:generator # generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。当然,上面这种不断调用next(g)实在是太变态了,正确的方法是使用for循环,因为generator也是可迭代对象 #我们创建了一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误。generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现 #著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到 def fib(max): n, a, b = 0, 0, 1 while n < max: print(b) a, b = b, a + b n = n + 1 return 'done' >>> fib(6) 1 1 2 3 5 8 'done' # 这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator函数,调用一个generator函数将返回一个generator
这些可以直接作用于for
循环的对象统称为可迭代对象:Iterable
。可以使用isinstance()
判断一个对象是否是Iterable
对象
可以被next()
函数调用并不断返回下一个值的对象称为迭代器:Iterator
生成器都是Iterator
对象,但list
、dict
、str
虽然是Iterable
,却不是Iterator
- map \ reduce
# map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回
>>> def f(x):
... return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]
# reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
>>> from functools import reduce
>>> def add(x, y):
... return x + y
...
>>> reduce(add, [1, 3, 5, 7, 9])
25
- 装饰器
# decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
@log
def now():
print('2015-3-25')
>>> now()
call now():
2015-3-25
# 把@log放到now()函数的定义处,相当于执行了语句
now = log(now)
#
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@log('execute')
def now():
print('2015-3-25')
# 因为返回的那个wrapper()函数名字就是'wrapper',所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。不需要编写wrapper.__name__ = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
- 偏函数
# 简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单
模块
每一个包目录下面都会有一个
__init__.py
的文件,这个文件是必须存在的,否则,Python 就把这个目录当成普通目录,而不是一个包。__init__.py
可以是空文件,也可以有 Python 代码,因为__init__.py
本身就是一个模块,而它的模块名就是mycompany
- scope
# 在一个模块中,我们可能会定义很多函数和变量,但有的函数和变量我们希望给别人使用,有的函数和变量我们希望仅仅在模块内部使用。在Python中,是通过_前缀来实现的 # 类似__xxx__这样的变量是特殊变量
在 Python 中,安装第三方模块,是通过包管理工具pip完成的,第三方库都会在 Python 官方的pypi.python.org网站注册
默认情况下,Python 解释器会搜索当前目录、所有已安装的内置模块和第三方模块,搜索路径放在
sys
模块的path
变量中类和实例 由于类可以起到模板的作用,因此,可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的
__init__
方法,在创建实例的时候,就把name
,score
等属性绑上去class Student(object): def __init__(self, name, score): self.name = name self.score = score # __init__方法的第一个参数永远是self,表示创建的实例本身 # 如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问 # 如果外部代码要获取name和score怎么办?可以给Student类增加get_name和get_score这样的方法,允许外部代码修改score怎么办?可以再给Student类增加set_score方法;你也许会问,原先那种直接通过bart.score = 99也可以修改啊,为什么要定义一个方法大费周折?因为在方法中,可以对参数做检查,避免传入无效的参数 class Student(object): ... def set_score(self, score): if 0 <= score <= 100: self.__score = score else: raise ValueError('bad score') # 双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过_Student__name来访问__name变量 >>> bart._Student__name 'Bart Simpson' # 但是强烈建议你不要这么干,因为不同版本的Python解释器可能会把__name改成不同的变量名
- 动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子
- 著名的“开闭”原则:对扩展开放:允许新增
Animal
子类;对修改封闭:不需要修改依赖Animal
类型的run_twice()
等函数
如果要获得一个对象的所有属性和方法,可以使用
dir()
函数,它返回一个包含字符串的 list,比如,获得一个 str 对象的所有属性和方法类似
__xxx__
的属性和方法在 Python 中都是有特殊用途的,比如__len__
方法返回长度。在 Python 中,如果你调用len()
函数试图获取一个对象的长度,实际上,在len()
函数内部,它自动去调用该对象的__len__()
方法,所以,下面的代码是等价的>>> len('ABC') 3 >>> 'ABC'.__len__() 3 # 我们自己写的类,如果也想用len(myObj)的话,就自己写一个__len__()方法 >>> class MyDog(object): ... def __len__(self): ... return 100 ... >>> dog = MyDog() >>> len(dog) 100
slots
# Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
>>> s = Student() # 创建新的实例
>>> s.name = 'Michael' # 绑定属性'name'
>>> s.age = 25 # 绑定属性'age'
>>> s.score = 99 # 绑定属性'score'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'
- @property
# Python内置的@property装饰器就是负责把一个方法变成属性调用的
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
# 要特别注意:属性的方法名不要和实例变量重名,是因为调用s.birth时,首先转换为方法调用,在执行return self.birth时,又视为访问self的属性,于是又转换为方法调用,造成无限递归,最终导致栈溢出报错RecursionError
- 定制类
# __str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,__repr__()是为调试服务的
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student object (name=%s)' % self.name
__repr__ = __str__
>>> class Student(object):
... def __init__(self, name):
... self.name = name
... def __str__(self):
... return 'Student object (name: %s)' % self.name
...
>>> print(Student('Michael'))
Student object (name: Michael)
- 枚举
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
# 这样我们就获得了Month类型的枚举类,可以直接使用Month.Jan来引用一个常量,或者枚举它的所有成员, value属性则是自动赋给成员的int常量,默认从1开始计数
# 如果需要更精确地控制枚举类型,可以从Enum派生出自定义类
from enum import Enum, unique
@unique
class Weekday(Enum):
Sun = 0 # Sun的value被设定为0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6
# @unique装饰器可以帮助我们检查保证没有重复值。
- TODO 元类 metaclass,当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例
# type()函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过type()函数创建出Hello类,而无需通过class Hello(object)...的定义
Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
IO
- open()
try:
f = open('/path/to/file', 'r')
print(f.read())
finally:
if f:
f.close()try:
f = open('/path/to/file', 'r')
print(f.read())
finally:
if f:
f.close()
# Python引入了with语句来自动帮我们调用close()方法:
with open('/path/to/file', 'r') as f:
print(f.read())
- 序列化
# 我们把变量从内存中变成可存储或传输的过程称之为序列化, 序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上, 把变量内容从序列化的对象重新读到内存里称之为反序列化
# Python提供了pickle模块来实现序列化
# JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便
# JSON标准规定JSON编码是UTF-8,所以我们总是能正确地在Python的str与JSON的字符串之间转换