参考资料
- 廖雪峰python https://www.liaoxuefeng.com/wiki/1016959663602400
Python一切皆对象
函数式编程
在python中,函数也是一个变量,可以指向不同的函数对象
map()
此处将函数抽象为变量
def f(x):
return x * x
r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])#这里的函数f被抽象调用了
print(list(r))
#结果
[1, 4, 9, 16, 25, 36, 49, 64, 81]
reduce()
from functools import reduce
def fn(x, y):
return x * 10 * y
def char2num(s):
digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
return digits[s]
print(reduce(fn, map(char2num, '13579')))
练习1
-
利用
map()
函数,把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字。输入:['adam', 'LISA', 'barT']
,输出:['Adam', 'Lisa', 'Bart']
:from functools import reduce def normalize(name): return name.capitalize() L1 = ['adam', 'LISA', 'barT'] L2 = list(map(normalize,L1)) print(L2)
-
Python提供的
sum()
函数可以接受一个list并求和,请编写一个prod()
函数,可以接受一个list并利用reduce()
求积:from functools import reduce def prod(L): def f(x, y): return x * y return reduce(f, L) print('3 * 5 * 7 * 9 =', prod([3, 5, 7, 9])) # 3 * 5 * 7 * 9 = 945
-
利用
map
和reduce
编写一个str2float
函数,把字符串'123.456'
转换成浮点数123.456
:from functools import reduce def str2float(s): def fn(x, y): return x * 10 + y def char2num(s): digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9} return digits[s] n = s.index('.'); s1 = list(map(char2num, s[:n])); s2 = list(map(char2num, s[n + 1:])); return reduce(fn, s1) + reduce(fn, s2) * 0.1 ** len(s2); print('str2float(\'123.456\') =', str2float('123.456')); if abs(str2float('123.456') - 123.456) < 0.00001: print('测试成功!'); else: print('测试失败!');
filter()
根据返回值是true或false来丢弃值
def is_odd(n):
return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]
sorted()
sorted()
函数也是一个高阶函数,它还可以接收一个key
函数来实现自定义的排序,
sorted([36, 5, -12, 9, -21], key=abs)
#[5, 9, -12, -21, 36]
函数作为返回值
函数作为方法返回,但是每次返回的函数不是同一个函数
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
f = lazy_sum(1,3,5,7,9)
print(f, f())
# <function lazy_sum.<locals>.sum at 0x0000021900796D30> 25
闭包的极端例子
返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
print(f1(), f2(), f3())
# 9 9 9
修改为,用函数的参数绑定循环变量当前的值
def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
return fs
f1, f2, f3 = count()
print(f1(), f2(), f3())
# 1 4 9
使用外层函数变量时,如果局部函数对外层变量赋值,编译器就会认为变量是局部变量,需要使用nonlocal进行声明
def inc():
x = 0
def fn():
# nonlocal x
x = x + 1
return x
return fn
f = inc()
print(f()) # 1
print(f()) # 2
练习2
利用闭包返回一个计数器函数,每次调用它返回递增整数:
def createCounter():
x = 0
def counter():
nonlocal x
x = x + 1
return x
return counter
f = createCounter()
print(f(), f(), f(), f(), f()) # 1 2 3 4 5
d = createCounter()
if [d(), d(), d(), d()] == [1, 2, 3, 4]:
print('测试通过!')
else:
print('测试失败!')
匿名函数
lambda x: x * x 这个表达式就是匿名函数,冒号前面的x
表示函数参数。
def f(x):
return x * x
g = lambda x: x * x
if f(2) == g(2):
print("相等")
# 相等
装饰器
由于log()
是一个decorator,返回一个函数,所以,原来的now()
函数仍然存在,只是现在同名的now
变量指向了新的函数,于是调用now()
将执行新函数,即在log()
函数中返回的wrapper()
函数。
def log(func): # 接收一个函数
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
@log # 相当于 now = log(now)
def now():
print('2015-3-25')
now()
在面向对象(OOP)的设计模式中,decorator被称为装饰模式。OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator。Python的decorator可以用函数实现,也可以用类实现。
decorator也可义传入参数,要编写一个返回decorator的高阶函数,写出来会更复杂。
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')
return 0
now()
#execute now():
#2015-3-25
但是以上的写法都是不完整的,输出函数的__name__就知道都被改变了
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')
return 0
print(now.__name__)
#wrapper
需要使用functools模块中的wraps(func)来解决,如下:
#不带参数
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
#带参数
import functools
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
练习3
请设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间:
# -*- coding: utf-8 -*-
import functools
import time
def metric(fn):
@functools.wraps(fn)
def wrapper(*args, **kw):
begin = time.time()
x = fn(*args, **kw)
end = time.time()
t = end - begin
print('%s executed in %s ms' % (fn.__name__, t))
return x
return wrapper
@metric
def fast(x, y):
time.sleep(0.0012)
return x + y
@metric
def slow(x, y, z):
time.sleep(0.1234)
return x * y * z
#这下面是测试的,没有输出是正确的
f = fast(11, 22)
s = slow(11, 22, 33)
if f != 33:
print('测试失败!')
elif s != 7986:
print('测试失败!')
# fast executed in 0.0029931068420410156 ms
# slow executed in 0.13463830947875977 ms
偏函数
# -*- coding: utf-8 -*-
import functools
# 创建偏函数functools.partial
int2 = functools.partial(int, base=2)
print(int2('1000000'))
# 提供额外的参数
max2 = functools.partial(max, 10)
print(max2(5, 6, 7))
模块
标准文件模板
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#表示模块的文档注释,任何模块代码的第一个字符串都被视为模块的文档注释
' a test module '
#定义作者的常用写法
__author__ = 'Qi Xiang Dong'
import sys
def test():
#sys.argv用list存储了命令行的参数
args = sys.argv
if len(args)==1:
print('Hello, world!')
elif len(args)==2:
print('Hello, %s!' % args[1])
else:
print('Too many arguments!')
#下面的包含的程序import后不会执行,必须以文件运行才会执行
if __name__=='__main__':
test()
作用域
__author__ #特殊变量
__abc && _abc #均为private 变量,不在模块外使用
第三方模块
https://pypi.org/
面向对象
类和对象
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
class People(object):
def eat(self):
pass
def learn(self):
pass
def sleep(self):
pass
# 继承了School类
class Student(People):
# 类似构造函数,self在构造时忽略,所有继承类都应该运行基类的__init__方法,此处因为超类m
def __init__(self, name, score):
# name和score为私有
self.__name = name
self.__score = score
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
def set_name(self, name):
self.__name = name
def set_score(self, score):
self.__score = score
def get_name(self):
return self.__name
def get_score(self):
return self.__score
# 重写了父类的方法,多态
def eat(self):
print("喜欢吃有营养的")
# 创建实例
bart = Student('Bart Simpson', 59)
lisa = Student('Lisa Simpson', 87)
bart.print_score()
lisa.print_score()
# bart.__name,不可执行
# Bart Simpson: 59
# Lisa Simpson: 87
当我们定义一个class的时候,我们实际上就定义了一种数据类型,我们定义的数据类型和Python自带的数据类型。判断一个变量是否是某个类型可以用isinstance()
判断
b = Animal() # b是Animal类型
c = Dog() # c是Dog类型
>>> isinstance(c, Dog)
True
>>> isinstance(c, Animal)
True
对扩展开放:允许新增Animal
子类;
对修改封闭:不需要修改依赖Animal
类型的run_twice()
等函数。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
class Animal(object): # 编写Animal类
def run(self):
print("Animal is running...")
class Dog(Animal): # Dog类继承Amimal类,没有run方法
pass
class Cat(Animal): # Cat类继承Animal类,有自己的run方法
def run(self):
print('Cat is running...')
class Car(object): # Car类不继承,有自己的run方法
def run(self):
print('Car is running...')
class Stone(object): # Stone类不继承,也没有run方法
pass
def run_twice(animal):
animal.run()
animal.run()
run_twice(Animal())
run_twice(Dog())
run_twice(Cat())
run_twice(Car())
run_twice(Stone())
# Animal is running...
# Animal is running...
# Animal is running...
# Animal is running...
# Cat is running...
# Cat is running...
# Car is running...
# Car is running...
# AttributeError: 'Stone' object has no attribute 'run'
获取对象信息
#type()获取对象信息
>>> type(123)
<class 'int'>
#比较是否是函数、表达式等
>>> type(fn)==types.FunctionType
True
>>> type(abs)==types.BuiltinFunctionType
True
>>> type(lambda x: x)==types.LambdaType
True
>>> type((x for x in range(10)))==types.GeneratorType
True
#isinstance()函数,判断是否是对应类以及其父类
>>> isinstance(d, Dog) and isinstance(d, Animal)
True
#isinstance()函数,判断是否是多个类型中的一种
>>> isinstance([1, 2, 3], (list, tuple))
True
#dir()获得对象的所有属性和方法,返回一个lsit
>>> dir('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']
#__len__() 和 len()等价
>>> len('ABC')
3
>>> 'ABC'.__len__()
3
#自己写len()函数
class MyDog(object):
def __len__(self):
return 100
dog = MyDog()
print(len(dog))
# getattr()、setattr()以及hasattr()操作对象
>>> hasattr(obj, 'x') # 有属性'x'吗?
True
>>> obj.x
9
>>> hasattr(obj, 'y') # 有属性'y'吗?
False
>>> setattr(obj, 'y', 19) # 设置一个属性'y'
>>> hasattr(obj, 'y') # 有属性'y'吗?
True
>>> getattr(obj, 'y') # 获取属性'y'
19
>>> obj.y # 获取属性'y'
19
#正确的用法的例子如下:
def readImage(fp):
if hasattr(fp, 'read'):
return readData(fp)
return Non
实例属性和类属性
- 实例属性即动态创建的类属性
- 类属性,类定义中有的属性
- **优先级:**实例属性 > 类属性
>>> class Student(object):
... name = 'Student'
...
>>> s = Student() # 创建实例s
>>> print(s.name) # 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性
Student
>>> print(Student.name) # 打印类的name属性
Student
>>> s.name = 'Michael' # 给实例绑定name属性
>>> print(s.name) # 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性
Michael
>>> print(Student.name) # 但是类属性并未消失,用Student.name仍然可以访问
Student
>>> del s.name # 如果删除实例的name属性
>>> print(s.name) # 再次调用s.name,由于实例的name属性没有找到,类的name属性就显示出来了
Student
面向对像高级
动态绑定
同一类的实例对象可以动态绑定方法,但是其他对象则不能使用,只有给类绑定方法,所有的实例对象才能使用。
class Student(object):
pass
def set_score(self, score):
self.score = score
Student.set_score = set_score #动态绑定一个函数
s = Student()
s.set_score(17)
print(s.score)
# 17
但同时,也希望能够限制不必的动态绑定
通过使用_slots_
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
使用__slots__
要注意,__slots__
定义的属性仅对当前类实例起作用,对继承的子类是不起作用的:
类的方法装饰器
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
class Screen(object):
# 特别注意,属性的方法名不要和实例变量重名,用width和_width区分
@property
def width(self):
return self._width # 如果为width,则被视为递归函数
@width.setter
def width(self, w):
self._width = w
@property
def height(self):
return self._height
@height.setter
def height(self, h):
self._height = h
@property
def resolution(self):
return self.width * self.height
# 测试:
s = Screen()
s.width = 1024
s.height = 768
print('resolution =', s.resolution)
if s.resolution == 786432:
print('测试通过!')
else:
print('测试失败!')
属性的方法名不要和实例变量重名是因为调用s.width
时,首先转换为方法调用,在执行return self.width
时,又视为访问self
的属性,于是又转换为方法调用,造成无限递归,最终导致栈溢出报错RecursionError
。
多继承
python 与c++相同都支持多继承,来满足多个类复杂关系的声明,同时有MixIn这种我
class Mammal(object):
pass
class RunnableMixIn:
pass
class CarnivorousMixIn:
pass
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
pass
定制类
_new_
_new_ 是一种负责创建类实例的静态方法,它无需使用 staticmethod 装饰器修饰,且该方法会优先 init() 初始化方法被调用。
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
class DemoClass:
instances_created = 0
def __new__(cls, *args, **kwargs):
print("__new__():", cls, args, kwargs)
instance = super().__new__(cls) #调用其超类的__new__,在
instance.number = cls.instances_created # 为demoClass类创建了一个新的属性number
cls.instances_created += 1
return instance
def __init__(self, attribute):
print("__init__():", self, attribute) # attribute 接收传进来的参数
self.attribute = attribute
test1 = DemoClass("abc")
test2 = DemoClass("xyz")
print(test1.number, test1.instances_created)
print(test2.number, test2.instances_created)
#输出
__new__(): <class '__main__.demoClass'> ('abc',) {}
__init__(): <__main__.demoClass object at 0x0000023681717B50> abc
__new__(): <class '__main__.demoClass'> ('xyz',) {}
__init__(): <__main__.demoClass object at 0x0000023681717A60> xyz
0 2
1 2
_str_
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student object (name: %s)' % self.name
print(Student('Michael'))
s = Student('Michael')
print(s)
Student object (name: Michael)
<__main__.Student object at 0x109afb310>
直接显示变量调用的不是__str__()
,而是__repr__()
,两者的区别是__str__()
返回用户看到的字符串,而__repr__()
返回程序开发者看到的字符串,也就是说,__repr__()
是为调试服务的。
_iter_
如果想使你的类支持像list或可以用for … in 循环的类,则需要_iter_,以斐波那契数列为例,写一个Fib类,可以作用于for循环:
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1 # 初始化两个计数器a,b
def __iter__(self):
return self # 实例本身就是迭代对象,故返回自己
def __next__(self):
self.a, self.b = self.b, self.a + self.b # 计算下一个值
if self.a > 100000: # 退出循环的条件
raise StopIteration()
return self.a # 返回下一个值
_getitem_
支持for循环,就想支持像list一样的取其中的元素
class Fib(object):
def __getitem__(self, n):
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
这部分还可以支持切片等的高级功能,但是需要自己写更多的代码,具体参考https://www.liaoxuefeng.com/wiki/1016959663602400/1017590712115904
枚举类
from enum import Enum, unique
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
# @unique装饰器可以帮助我们检查保证没有重复值。
@unique
class Weekday(Enum):
Sun = 0 # Sun的value被设定为0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6
# value属性则是自动赋给成员的int常量,默认从1开始计数。
for name, member in Month.__members__.items():
print(name, '=>', member, ',', member.value)
print(Weekday.Sun)
print(Weekday.Sun.value)
Jan => Month.Jan , 1
Feb => Month.Feb , 2
Mar => Month.Mar , 3
Apr => Month.Apr , 4
May => Month.May , 5
Jun => Month.Jun , 6
Jul => Month.Jul , 7
Aug => Month.Aug , 8
Sep => Month.Sep , 9
Oct => Month.Oct , 10
Nov => Month.Nov , 11
Dec => Month.Dec , 12
Weekday.Sun
0
使用元类
在python中class的定义是运行时动态创建的,而创建class的方法就是使用type()
函数。因此type函数可以创建类,无需用class定义
def fn(self, name='world'): # 先定义函数
print('Hello, %s.' % name)
Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
h = Hello()
h.hello()
要创建一个class对象,type()
函数依次传入3个参数:
- class的名称;
- 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
- class的方法名称与函数绑定,这里我们把函数
fn
绑定到方法名hello
上。
这种方法和使用class来创建是完全相同的
metaclass
可以把类看成是metaclass创建出来的“实例”。
连接起来就是:先定义metaclass,就可以创建类,最后创建实例。
我参考了这个文章,类与元类(metaclass)的理解 。讲的比较透彻有补充再来添加。
错误处理
和大多数高级语言相同,python也有自己的错误处理函数
try:
print('try...')
r = 10 / int('2')
print('result:', r)
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
else:
print('no error!')
finally:
print('finally...')
print('END')
python的错误类型也都是class,存在继承关系,高的类型会把低的类型覆盖。
常见的错误类型和继承关系看这里:
https://docs.python.org/3/library/exceptions.html#exception-hierarchy
通过函数调用栈,来看报错信息,从而找到错误的位置
# err.py:
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
bar('0')
main()
#输出
Traceback (most recent call last):
File "E:\Python_item\Item1\test.py", line 11, in <module>
main()
File "E:\Python_item\Item1\test.py", line 9, in main
bar('0')
File "E:\Python_item\Item1\test.py", line 6, in bar
return foo(s) * 2
File "E:\Python_item\Item1\test.py", line 3, in foo
return 10 / int(s)
ZeroDivisionError: division by zero
使用logging()函数可以记录错误
文件读写
参考https://www.liaoxuefeng.com/wiki/1016959663602400/1017607179232640
栈
https://docs.python.org/zh-cn/3/tutorial/datastructures.html#using-lists-as-stacks
python的数据结构都用类来封装
class Stack:
def __init__(self):
self.stack = []
#入栈
def push(self, value):
self.stack.append(value)
#出栈
def pop(self):
return self.stack.pop()
#得到栈顶元素
def get_top(self):
if len(self.stack) > 0:
return self.stack[-1]
else:
return None
x = Stack()
但其实这样做完就发现完全不需要用class来建立堆栈,只需要用list足够了,类似队列的操作也可以用类来封装