参考资料
Python一切皆对象
函数式编程
在python中,函数也是一个变量,可以指向不同的函数对象
map()
此处将函数抽象为变量
def f (x ): return x * x r = map (f, [1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ]) print (list (r))[1 , 4 , 9 , 16 , 25 , 36 , 49 , 64 , 81 ]
reduce()
from functools import reducedef 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 reducedef 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 reducedef prod (L ): def f (x, y ): return x * y return reduce(f, L) print ('3 * 5 * 7 * 9 =' , prod([3 , 5 , 7 , 9 ]))
利用map
和reduce
编写一个str2float
函数,把字符串'123.456'
转换成浮点数123.456
:
from functools import reducedef 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 ]))
sorted()
sorted()
函数也是一个高阶函数,它还可以接收一个key
函数来实现自定义的排序,
sorted ([36 , 5 , -12 , 9 , -21 ], key=abs )
函数作为返回值
函数作为方法返回,但是每次返回的函数不是同一个函数
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())
闭包的极端例子
返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
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())
修改为,用函数的参数绑定循环变量当前的值
def count (): def f (j ): def g (): return j*j return g fs = [] for i in range (1 , 4 ): fs.append(f(i)) return fs f1, f2, f3 = count() print (f1(), f2(), f3())
使用外层函数变量时,如果局部函数对外层变量赋值,编译器就会认为变量是局部变量,需要使用nonlocal 进行声明
def inc (): x = 0 def fn (): x = x + 1 return x return fn f = inc() print (f()) print (f())
练习2
利用闭包返回一个计数器函数,每次调用它返回递增整数:
def createCounter (): x = 0 def counter (): nonlocal x x = x + 1 return x return counter f = createCounter() print (f(), f(), f(), f(), f()) 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 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()
但是以上的写法都是不完整的,输出函数的__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__)
需要使用functools模块中的wraps(func)来解决,如下:
import functoolsdef log (func ): @functools.wraps(func ) def wrapper (*args, **kw ): print ('call %s():' % func.__name__) return func(*args, **kw) return wrapper import functoolsdef 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,它可作用于任何函数上,并打印该函数的执行时间:
import functoolsimport timedef 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 ('测试失败!' )
偏函数
import functoolsint2 = functools.partial(int , base=2 ) print (int2('1000000' ))max2 = functools.partial(max , 10 ) print (max2(5 , 6 , 7 ))
模块
标准文件模板
' a test module ' __author__ = 'Qi Xiang Dong' import sysdef test (): args = sys.argv if len (args)==1 : print ('Hello, world!' ) elif len (args)==2 : print ('Hello, %s!' % args[1 ]) else : print ('Too many arguments!' ) if __name__=='__main__' : test()
作用域
第三方模块
https://pypi.org/
面向对象
类和对象
class People (object ): def eat (self ): pass def learn (self ): pass def sleep (self ): pass class Student (People ): def __init__ (self, 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()
当我们定义一个class的时候,我们实际上就定义了一种数据类型,我们定义的数据类型和Python自带的数据类型。判断一个变量是否是某个类型可以用isinstance()
判断
b = Animal() c = Dog() >>> isinstance (c, Dog)True >>> isinstance (c, Animal)True
对扩展开放:允许新增Animal
子类;
对修改封闭:不需要修改依赖Animal
类型的run_twice()
等函数。
class Animal (object ): def run (self ): print ("Animal is running..." ) class Dog (Animal ): pass class Cat (Animal ): def run (self ): print ('Cat is running...' ) class Car (object ): def run (self ): print ('Car is running...' ) class Stone (object ): pass def run_twice (animal ): animal.run() animal.run() run_twice(Animal()) run_twice(Dog()) run_twice(Cat()) run_twice(Car()) run_twice(Stone())
获取对象信息
>>> 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))>>> hasattr (obj, 'x' ) True >>> obj.x9 >>> hasattr (obj, 'y' ) False >>> setattr (obj, 'y' , 19 ) >>> hasattr (obj, 'y' ) True >>> getattr (obj, 'y' ) 19 >>> obj.y 19 def readImage (fp ): if hasattr (fp, 'read' ): return readData(fp) return Non
实例属性和类属性
实例属性即动态创建的类属性
类属性,类定义中有的属性
**优先级:**实例属性 > 类属性
>>> class Student (object ):... name = 'Student' ... >>> s = Student() >>> print (s.name) Student >>> print (Student.name) Student >>> s.name = 'Michael' >>> print (s.name) Michael >>> print (Student.name) Student >>> del s.name >>> print (s.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)
但同时,也希望能够限制不必的动态绑定
通过使用_slots _
class Student (object ): __slots__ = ('name' , 'age' )
使用__slots__
要注意,__slots__
定义的属性仅对当前类实例起作用,对继承的子类是不起作用的:
类的方法装饰器
class Screen (object ): @property def width (self ): return self._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 () 初始化方法被调用。
class DemoClass : instances_created = 0 def __new__ (cls, *args, **kwargs ): print ("__new__():" , cls, args, kwargs) instance = super ().__new__(cls) instance.number = cls.instances_created cls.instances_created += 1 return instance def __init__ (self, attribute ): print ("__init__():" , self, 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 > xyz0 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 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, uniqueMonth = Enum('Month' , ('Jan' , 'Feb' , 'Mar' , 'Apr' , 'May' , 'Jun' , 'Jul' , 'Aug' , 'Sep' , 'Oct' , 'Nov' , 'Dec' )) @unique class Weekday (Enum ): Sun = 0 Mon = 1 Tue = 2 Wed = 3 Thu = 4 Fri = 5 Sat = 6 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)) 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
通过函数调用栈,来看报错信息,从而找到错误的位置
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足够了,类似队列的操作也可以用类来封装