参考资料

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

  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)
  2. 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
  3. 利用mapreduce编写一个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个参数:

  1. class的名称;
  2. 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
  3. 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足够了,类似队列的操作也可以用类来封装