python 是一种解释型语言, 无需编译就可以利用解释器执行. 支持函数式编程和面向对象编程, 拥有动态类型系统和垃圾回收功能. 相比于java, python 以文件而不是类为基本单位, 一个python 文件里, 可以直接定义函数, 或者多个类, 或者直接的代码行, 运行python 文件会执行没有被函数或者类包裹的代码行, 相当于省略了java 里的main 函数
python
# start.py
print('hello world')
数据类型
💡python 拥有动态类型, 因此定义变量时直接a = 1 这样赋值就好了, 解释器会自动确认a的类型, 另外a 的是数据类型不是固定的, 可以继续给a 赋值其他类型的值(有点过于灵活了)
数字|Number
python 中统一使用int 表示整数, 用float 表示小数, 它们在64位系统中占用8个字节(对应java 语言的long 和double)
python
2 / 4
# >>0.5
2 // 4 #整除
# >>0
17 % 3 #取余
# >>2
2 ** 5 #乘方
# >>32
bool 类型只有两个取值: True 和False, 因为是int 的子类, 它可以直接当作数字参与运算, True 被视为1, False 被视为0
python
True == 1
# >>True
True + 1
# >>2
bool 类型经常和逻辑运算符一起使用, 比如and, or 和not
python
a = 2 < 3
# >>True
b = 2 == 3
# >>False
a and b
# >>False
not a
# >>False
另外, python 中所有非0 的数字和非空的字符串, 列表, 元组等数据都被视为True, 0, 空字符串, 空列表, 空元组等被视为False
字符串|String
python 中字符串是用单引号或者双引号包裹的任意文本, 同时支持用反斜杠符号转义
python
# 字符串里包含"或者'时, 要使用\转义
a = "i\'m ok!"
# '''...''' 可以表示多行文本
print('''line1
line2
line3''')
如果希望字符串不进行任何转义原样输出, 可以使用r'字符串' 声明字符串
python
# 从左到右, 第一个反斜杠是为了帮第二个反斜杠转义(因为\是特殊符号), 代表原样输出一个\, 第三个反斜杠与t组合输出一个制表符
print('制表符\\\t\\')
#>>>制表符\ \
#加r以后将原样输出
print(r'\\\t\\')
#>>>\\\t\\
字符串可以像数组一样截取, 格式是:变量[包含上标:不包含下标], 0表示第一个元素, 1表示第二个元素, 以此类推;-1 表示最后一个元素, -2表示倒数第2个元素, 以此类推
python
str = 'hello'
# 输出第一个字符h
sub_str = str[0]
# 输出从第一个字符开始后面的所有字符hello
sub_str = str[0:]
# 输出从第一个字符到第二个字符py
sub_str = str[0:2]
# 输出从第一个字符到倒数第二个字符hell
sub_str = str[0:-1]
# 输出从倒数第四到倒数第一之间的字符ell
sub_str = str[-4:-2]
字符串可以使用* 表示复制当前字符串, 用+ 连接两个字符串
python
str = 'hello'
# 两个str, hellohello
sub_str = str * 2
# helloworld
sub_str = str + 'world'
字符串支持使用%占位符的方式进行格式化, 支持%d(整数),%f(小数),%s(字符串), %x(十六进制整数), 如果不确定就用%s即可
- % 方式
python
'name:%s age:%d' % ('ted', 10)
- 使用format()函数
python
'name:%s age:%d'.format('ted', 10)
- 使用f'字符串, 格式是:f"字符串{要填入的参数:格式化参数}" 或者省略格式化参数: f"字符串{要填入的参数:格式化参数}". 下述代码中, 定义了两个占位符, 第一个直接传入了变量r, 第二个传入了变量s, 并对s进行了格式化--保留了2位小数
python
r = 2.5
s = 3.14 * r ** 2
print(f'The area of the circle with radius {r} is {s:.2f}')
💡 如果要输出%本身, 需要用%%的方式转义
python
str = 'age:%d name:%s'
str %
数组|tuple
tuple 是不可变的有序列表, 一旦初始化就不能修改, 使用()
声明.
💡 tuple 中只有一个元素时要加上逗号, 防止歧义
python
classmates = ('a', 'b', 'c')
# 只有一个元素时要加上逗号, 防止歧义
names = (1, )
# 访问元素a
classmates[0]
# 倒序访问c
classmates[-1]
# 将classmates 中第0, 1, 2个元素非别赋值给a, b, c
a, b, c = classmates
列表|list
列表是可变的集合, 可以随时添加和删除其中的元素, 使用[]
声明
python
# 声明
classmates = ['a', 'b', 'c']
# 获取长度
len(classmates)
# 访问第0个元素
classmates[0]
# 获取值为'x' 的元素索引, 找不到就报错
a = classmates.index('x')
# 同上, 只是限制了查找范围
a = classmates.index('x', 1, 5)
# 在指定位置插入元素
classmates.insert(1, 'd')
# 在列表末尾插入元素
classmates.append('d')
# 删除并返回最后一个元素
a = classsmates.pop()
# 删除并返回指定index 的元素
a = classmates.pop(1)
# 删除第一个值为x 的元素, 未找到时会报异常
classmates.remove('e')
# 修改第1个元素
classsmates[1] = 'e'
list 支持嵌套, 类似二维数组
python
s = ['python', 'java', ['asp', 'jsp']]
# 访问元素
s[1]
s[2][1]
字典|dict
字典用于存储键值对, 无序集合, 使用{}
声明
python
d = {'k1': 'v1', 'k2': 'v2'}
# 可以直接获取值和赋值
d['k1']
d['k2'] = 'vv2'
# 也可以使用get() 获取, 并提供默认值
d.get('k3', 'v3')
# 用pop() 删除元素
d.pop('k3')
# 用in 判断是否包含key
'k5' in d
💡建议使用get() 方法获取值, 因为如果dict 不存在'k1, d['k1']会报错
集合|set
set 可以看作是只有key 的dict, 它同样是无序不可重复的, 通过set()
声明, set()
函数可以接收iterable 参数, 比如list, string
python
# 接收list参数
s = set([1, 2, 3])
# 接收字符串
s = set('a1b2c3')
s.remove(1)
s.add(2)
set 可以做数学意义上的交集, 并集等操作
python
s1 = set([1, 2, 3])
s2 = set([2, 3, 4])
# 交集, 输出{2, 3}
s1 & s2
# 并集, 输出{1, 2, 3, 4}
s1 | s2
流程控制
条件判断|if
相比Java, Python 的各种结构体都不需要{}, 依靠缩进来确定结构
python
age = 20
if age >= 18:
print('adult')
elif age >= 6:
print('teenager')
else:
print('kid')
模式匹配|match case
如果if 条件较多, 可以使用match 语句改写
python
score = 'B'
match score:
case 'A':
print('A')
case 'B'
print('B')
# 默认
case _:
print('C')
case
后面还可以匹配多个值, 或者加条件判断
python
age = 15
match age:
# 创建一个变量x
case x if x < 10:
print(f'< 10 years old: {x}')
case 11 | 12 | 13:
print('11 ~ 13 years old')
match
语句可以匹配列表
python
args = ['gcc', 'hello.c', 'world.c']
match args:
case ['gcc']
print('missing params')
case ['gcc', file1, *files]:
print('gcc ' + file1 + ','.join(files))
当args
元素与['gcc'] 相同时, 会进入第一个case
. 第二个case
, 要求第一个字符串必须是'gcc', 第二个字符串会赋值给file1, 其余字符串赋值给files, files是一个可变参数
循环|for|while
Python 支持for...in循环 和while 循环
python
friends = ['dog', 'cat']
for i in friends:
print(i)
i = 0
while i < len(friends):
print(friends[i])
和java 一样, python 也指出break
和continue
, 其中break
用来结束当前循环, continue
用来跳过一轮循环, 继续执行下一轮循环
函数
函数定义
python 中使用def
关键字指定函数, 因为参数无法指定类型, 可能需要在函数内部增加入参校验; 另外, 返回值可以有多个, python 会自动将多个返回值转化成一个tuple
python
def test(x, y):
nx = x + y
ny = x - y
return nx, ny
nx, ny = test(1, 1)
默认参数
python 的函数入参可以设置默认值, 作为可选参数, 没有默认值的作为必选参数, 不能省略
python 的参数可以按声明顺序依次填入, 也可以通过指定参数名乱序传入
python
def test(x, y, z=1)
return x, y, z
test(1, 2)
test(1, 2, z=3)
python 的默认参数作用域是函数, 也就是说函数定义出来以后, 默认值就被初始化了, 所有调用该函数的代码, 共用一个参数, 因此默认参数必须使用不可变类型: string, tuple 等
python
def print_list(x, l = []):
l.add(x)
print(l)
print_list(1)
# >>> [1]
print_list(2)
# >>> [1, 2]
可变参数
python 函数支持设置数量可变的参数, 使用*标记, 函数会将可变参数视为tuple
python
def add(*args):
ret = 0
for i in args:
ret += i
return ret
add(1, 2, 3)
# 加星号表示当前数组作为可变参数传入
add(*[1, 2, 3])
关键字参数
关键字参数与可变参数类似, python 会在函数内部转变成dict
python
def add(x, y, **kw):
print(x, y, kw)
add(1, 2, z=3)
# >>> 1, 2, {'z': 3}
# 加两个星号python 会自动将当前dict 转换成键值对
add(1, 2, **{'z': 3})
如果想要限制key, 可以这样写:
python
# 星号后面代表关键字参数的key
def add(a, b, *, c, d)
print(a, b, c, d)
add(1, 2, c=3, d=4)
高阶函数之函数参数
python 支持将函数赋值给一个变量, 同时这个变量可以作为参数传递给另一个函数
python
def add(x, y, f):
return f(x) + f(y)
# 定义变量z 指向函数abs
z = abs
# 可以通过z 实现abs的功能
print(z(-10))
# >>> 10
# 调用高阶函数add
ret = add(1, -1, z)
print(ret)
# >>> 2
python 内置了很多这样的高级函数, 例如map, reduce, filter....
map
map() 是python 的内建函数, 用来指向映射功能, 它接收两个参数, 一个是函数,一个是Iterable, map 通过遍历Iterable 参数, 依次执行传入的函数, 将该Iterable 参数映射成Iterator 返回.
💡 传入的是itreable, 返回的是iterator
python
def f(x):
return x * x
ret = map(f, [1, 2, 3, 4, 5])
list(r)
>>> [1, 4, 9, 16, 25]
reduce
reduce() 将一个函数作用在一个序列上, 并将函数运行结果继续和序列的下一个元素做累积计算. reduce(f, [x1, x2, x3]) = f(f(f(x1, x2), x3), x4)
python
from functools import reduce
def fn(x, y):
return x * 10 + y
reduce(fn, [1, 3, 5, 7, 9])
# >>> 13579
filter
filter() 用于过滤序列
python
def is_odd(n):
return n % 2 == 1
ret = filter(is_odd, [1, 2, 3])
list(ret)
>>> [1, 3]
sorted
sorted() 用于排序, 接收一个序列, 一个排序函数key, 一个是否反转reverse
python
sorted([1, 7, -5, 3])
# >>> [-5, 1, 3, 7]
sorted([1, 7, -5, 3], key=abs)
# >>> [1, 3, 5, 73751]
sorted([1, 7, -5, 3], key=abs, reverse=True)
# >>> [7, 5, 3, 1]
高阶函数之返回函数
除了将函数作为变量, 还可以在函数内部定义函数, 并将它作为返回值.这种程序结构被称为
闭包
python
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
# 调用lazy_sum 会返回一个函数
f = lazy_sum(1, 2, 3)
# >>> <function lazy_sum.<locals>.sum at 0x101c6ed90>
# 调用f 才会返回计算结果
ret = f()
# >>> 25
💡 返回的函数并没有被立刻执行, 被调用后才会真正执行. 因此返回函数应该避免使用后续会发生变化的变量
python
# 函数count 内循环创建函数f, 输出i 的vi
def count():
fs = []
for i in range(1, 4):
def f():
return i
fs.append(f)
return fs
f1, f2, f3 = count()
# 调用f1, f2, f3, 并没有返回1, 2, 3, 而是都返回了3
# 这是因为f 函数并没有立刻执行, 而是在调用时才会执行, 此时i 的值已经是3了
f1()
# >>> 3
f2()
# >>> 3
f3()
# >>> 3
💡 内层函数只能读取外层函数的变量, 不能直接修改. 如果要修改, 必须先用nonlocal 声明
python
# 函数count 内循环创建函数f, 输出i 的vi
def count():
fs = []
x = 1
for i in range(1, 4):
def f():
# nonlocal i
x = x + i
return x
fs.append(f)
return fs
f1, f2, f3 = count()
f1()
# >>> 报错
# 解释器会把x 当作f() 的局部变量, 它并没有被初始化, 必须使用nonlocal x
# 告诉解释器x 是外层函数的变量
匿名函数
匿名函数使用关键字lambda, 只能有一个表达式, 代表返回值
python
lambda x : x * x
# 等价于
def f(x):
return x * x
装饰器
装饰器(decorator)可以在代码运行期间动态增加功能. 本质上, 装饰器是一个返回函数的高阶函数, 使用@ 符号来标识要增强的函数
python
# 定义装饰器
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
# 使用装饰器
@log
def now():
print('222')
now()
# >>> call now():
# >>> 222
调用now() 函数, 不仅会运行now() 函数本身, 还会在运行之前执行log() 函数. 把@log 放到now() 函数的定义处, 相当于执行了now = log(now), 此时now 变量指向了新的函数, 于是调用now() 将执行新的逻辑
如果想让decorator 接入参数, 需要编写一个返回decorator 的高阶函数.
python
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s()' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@log('execute')
def now():
print('2023-10-11')
now()
# >>> execute now(): 2023-10-11
这段代码首先执行log('execute'), 返回decorator 函数, 再调用返回的函数, 参数是now 函数, 返回值是wrapper 函数. 因为最后返回的是wrapper 函数, 这导致now.name 会返回'wrapper', 可以用functools.wraps 来处理
面向对象
python 使用class
关键字声明类,创建实例使用类名() 💡类中定义的函数, 第一个参数必须是self, 代表当前实例
python
class Cat(object):
def miaomiao(self):
print('miaomiao')
cat = Cat()
cat.miaomiao()
封装
python 中, 类的构造函数使用__init__
声明. python作为动态语言, 也可以直接给一个实例变量增加属性
python
class Cat:
# 类属性, 所有实例共用
class_name = 'cat'
def __init__(self, name):
self.name = name
def print_name(self):
print(self.name)
cat = Cat("mimi")
# 手动绑定age 属性
cat.age = 1
如果想声明私有属性, 可以在属性名称前面加上两个下划线
python
class Cat:
def __init__(self, name):
self.__name = name
def set_name(self, name):
self.__name = name
def get_name(self):
return self.__name
继承
类名后面的括号用来注明它的父类, 默认继承object类, 可以省略. 另外, python 支持多重继承
python
class Animal:
def eat(self):
print('Animal is eating')
class Pet:
def play(self):
print('Pet is playing')
class Cat(Animal, Pet):
pass
cat = Cat()
cat.eat()
cat.play()
多态
基于继承, 可以将一个类重写为多个子类, 子类的数据类型可以被看作父类, 但是他们有不同的表达
python
def test(animal):
animal.eat()
cat = new Cat()
test(cat)
python 的多态更加自由, 入参没有限制类型, 只要它有eat()
方法就行
并发编程
协程
协程是一种特殊的函数, 可以在内部中断, 然后在再次被调用时, 从上次中断的地方继续执行 python 的协程基于generator 实现, 通过yield
(暂停并返回)来标记中断点, 从而创建一个协程
python
def consume():
ret = 'start'
while True:
#程序在此处阻塞, 直接返回ret, 下次调用才会执行param = ret
param = yield ret
if not param:
return
print('CONSUMER consuming %s ' % param)
ret = 'end'
def produce(c):
# 启动并调用generator
c.send(None)
param = 0
while param < 3:
param += 1
print('PRODUCER producing %s ' % n)
# 调用generator
ret = c.send(param)
print('PRODUCER consumer return %s' % ret)
# 关闭generator
c.close()
c = consume()
produce(c)
>>> PRODUCER producing 1
>>> CONSUMER consuming 1
>>> PRODUCER consumer return end
>>> PRODUCER producing 2
>>> CONSUMER consuming 2
>>> PRODUCER consumer return end
>>> PRODUCER producing 3
>>> CONSUMER consuming 3
>>> PRODUCER consumer return end
>>> ...
上述代码的执行流程如下:
- 创建生成器c, 然后调用produce 方法, 并传入参数c
- 启动生成器c, 并返回ret, consume 函数进入暂停
- produce 函数进入while 循环, param 被赋值为1, 打印param, 然后将其发送给consume
- consume 接收到param, 然后执行打印, ret 被赋值为end, 再次进入循环, 返回ret, 然后进入暂停
- produce函数执行第二个打印, 然后进入第二轮循环
- ...
asyncio
python 提供了asyncio
来实现异步IO. 通过创建一个消息循环EventLoop, 将需要执行的协程放入其中, 来实现异步IO 它有两种写法, 具体如下:
python
import asyncio
@asyncio.coroutine
def hello():
print("hello")
# 模拟耗时操作
r = yield from asyncio.sleep(1)
print("world")
async def test():
print("hello")
r = await asyncio.sleep(1)
print("world")
loop = asyncio.get_event_loop()
# loop.run_until_complete(hello())
loop.run_until_complete(asyncio.wait([hello(), test()]))
loop.close()
第一种写法里使用@asyncio.coroutine
声明协程, 然后使用yield from
来调用另一个协程并获取返回值
高级特性
切片
切片操作符可以很方便地获取序列型对象(如string, list, tuple) 的部分元素, 语法是: 起始包含:截止不包含:step , 每个参数都可以省略, 如果参数过大或过小, python会自动调整
python
s = [0, 1, 2, 3, 4, 5]
# 从index 1到index5(不包含), 每2个元素截取一次
s1 = s[0:5:2]
# >>[0, 2, 3]
# step 为负数时, 逆向索引
s2 = s[0:5:-2]
# >>[4, 2, 0]
迭代
python 中可迭代对象(Iterable 子类)都可以通过for ... in 实现迭代
python
# 迭代字典key
for key in dicts:
# 迭代字典value
for value in dicts.values():
# 迭代字典key 和value
for key, value in dicts.items():
# 迭代list, 并获取下标, 可以用enumerate 函数
for index, value in enumerate([1, 2, 3])
列表生成式
列表生成式可以创建基于特定规则的列表, 格式为: [表达式 遍历 筛选条件]
python
[x * 2 for x in range(1, 11) if x % 2 == 0]
# >>[4, 8, 12, 16, 20]
# 可以两层嵌套循环
[m + n for m in 'ABC' for n in 'XYZ']
# >>['AX', 'AY', 'AZ'...]
生成器-generator
生成器用于创建生成规则, 注意是列表生成规则而不是列表, 格式是: (表达式 遍历 筛选条件) , 语法上与列表生成式类似, 方括号变成了小括号. 通过next() 函数打印下一个元素, 也可以使用for 循环遍历
python
g = (x * 2 for x in range(1, 11) if x % 2 == 0)
next(g)
# >>4
for n in g:
print(n)
# >>4, 8, 12, 16, 20
还可以通过yield 关键字创建generator 函数, 调用generator 函数将获得一个generator, 每次调用next() 或者遍历, 会返回yield 处的值
python
def fib(max):
n = 0
while n < max:
yield n
n += 1
return 'done'
f = fib(3)
for n in f:
print(n)
# >>0 1 2
想要获取generator 函数的值, 需要遍历到最后一个值, 捕获StopIteration 错误
python
g = fib(6)
while True:
try:
x = next(g)
except StopIteration as e:
print(e.value)
# >>done