1.一等函数: 运行时候创建,
- 可以给变量赋值;
- 也可以作为函数的返回结果;
def factorial(n): """ return n """ return 1 if n<2 else n*factorial(n-1) fact = factorial print(fact(20))
def make_average(): #返回一个函数; num = [] def average(value): num.append(value) return sum(num)/len(num) return average avg=make_average() avg(3)
2. 高阶函数 + 列表/字典推导:
- 接受函数作为参数
- 或者函数作为返回结果 map/reduce/filter/sorted #注意这里的map是根据提供的函数对指定的序列做映射的高阶函数,dict 是字典;
usernames = ["pear","apple","peach","cherry"] num_list=[2,3,1,5,4,9] cal_list = list(map(factorial,num_list)) #map 作为高阶函数,可以接收函数作为参数 cal_list1 =[factorial(x) for x in num_list] ##列表推导 gen_fun =(x*x for x in num_list) ##generator print(cal_list,cal_list1) print(next(gen_fun)) #next result = {factorial(num):num for num in num_list} #函数作为参数传到高阶函数中,字典推导 与 上面列表推导类似 print(result) print(list(map(factorial,filter(lambda x: x%2, num_list)))) ## 通过 print(callable(factorial)) #查看是否可以调用 print(sorted(usernames,key=lambda x:x[0]))
callable(sort) #查看是否可以被调用<br>def __call__(self): #类定义中包含这个函数时候,对象可以直接调用(p1()), 上面callable 返回也会是true;
那么对于一个自定义的类,如何实现该类的实例化对象的可调用;定义def __call__(self) :
这样自定义的类实例,一样可以传递到这些高阶函数中使用;
class Surprise(): def __init__(self,items): self._items = items def pick(self): try: return self._items.pop() except Exception as e: print(str(e)) def __call__(self,var=0): #return self.pick() self._items.append(var) return self._items s = Surprise([1,2,3,4]) print(s()) print(callable(s)) m_1 = map(s,[12,3,4,5]) for i in m_1: print(i)
EXAMPLE2:
#生成HTML标签 #<p>hellp</p> #<item clase = '' size='small'>a</item> def make_element(name,*content,cls=None, **kwargs): #不确定参数 if cls: kwargs['class'] = cls pairs = [f"{attr}={value}" for attr, value in kwargs.items()] #字符串格式化 attr_str = ' '.join(pairs) #join使用 if not content: return f"<{name}{attr}/>" elements = [f"<{name} {attr_str}>{con}<{name}/>]" for con in content] #字符串格式化 return '\n'.join(elements) print(make_element("p","hello","word",cls="clear",size="small",color="red"))
3.打包/解包:
d1={"a" : 1, "b" : 2} d2 ={"c" : 3, "d" : 4} #new_d = d1 + d2 #dict不支持 + 操作, list 可以 new_d = {**d1, **d2} #这里就是使用打包/解包操作,类似不确定参数传递
4. 装饰器: 参考link
- 以目前的理解,装饰器其实就是利用上面的高阶函数,一层层的调用的函数;
- 对于这些层层调用函数中的参数,如果存在额外的参数要求,通过多包装几个层级来实现;
- 最里面的两个层级是固定的:
- 最里层具体的加装部分,参数与被装饰函数相同,或者使用不确定参数;
- 次里层参数就是被装饰函数,返回就是里层装饰过的函数;这里也是使用@wraps保存原函数信息层级;@wraps(func)
函数中增加装饰器基础版:
import time def build_func_with_time_record(func): '''here is the build func ''' def wrap(*args, **kwargs): '''here is the wrap ''' start = time.time() result = func(*args, **kwargs) end = time.time() print('function {} total cost time {}'.format(func.__name__, end - start)) return result return wrap @build_func_with_time_record def test(a, b): ''' here is the test func ''' time.sleep(2) return a + b test(12,45) help(test) test.__name__ test.__doc__
结果可以看到这方法会丢失原始函数的相关信息; 可以通过使用functools 中的wraps 将原始函数信息保存下来;在传递func 参数内进行wrap;
import time from functools import wraps #diff def build_func_with_time_record(func): '''here is the build func ''' @wraps(func) #diff def wrap(*args, **kwargs): '''here is the wrap ''' start = time.time() result = func(*args, **kwargs) end = time.time() print('function {} total cost time {}'.format(func.__name__, end - start)) return result return wrap @build_func_with_time_record def test(a, b): ''' here is the test func ''' time.sleep(2) return a + b test(12,45) help(test) test.__name__ test.__doc__
有专门的方法查看函数的签名
from inspect import signature signature(test)
original result:
Updated result:
对于装饰器来讲,如何传递额外参数进装饰器,比如信息打印等级;
def log1(level=None,message=None): def decorator(func): def wrapper(*arg,**kw): if level: print(f'log leve{level}, default head is {message}') return func(*arg,**kw) return wrapper return decorator @log1() def func1(a,b): return (a+b) @log1('leve1', "DEBUG") def func2(a,b): return (a * b) print(func1(1,4)) print(func2(5,34))
python 中自带了一个logging, 下面是简单的例子;
import logging from functools import wraps
from operator import add, mul logging.basicConfig(format="%(asctime)s %(name)s %(lineno)d %(levelname)s %(message)s") logging.root.setLevel(logging.NOTSET) def log(level,name=None, message=None): def decorator(func): logname = name if name else func.__name__ log = logging.getLogger(logname) logmsg = message if message else f"function <{func.__name__}> is running..." @wraps(func) def wrapper(*args,**kwargs): log.log(level,logmsg) # print(logmsg) return func(*args,**kwargs) return wrapper return decorator @log(level=logging.DEBUG) def func_cat(tx1,tx2,*arg,**kwarg): print(type(arg)) print(kwarg) func_cat(1,3,5,6,8,a=5) print(add(12,5)) time.sleep(2) func_cat(1,3,5,8,12,x=5)
关于 python @property 简单介绍:这是python自带的一种装饰器,用来修饰方法的,主要是用来创建只读属性,@property 装饰器可以把方法转换成相同名称的只读属性,可以与定义的属性配合使用,防止被修改;
class HUMAN: def __init__(self, name, age): self._name=name self._age_readOnly=age def get_name(self): return self._name @property def age(self): return self._age_readOnly class STUDENT(HUMAN): def __init__(self,name, age, grade): HUMAN.__init__(self, name, age) self._grade = grade def do_homework(self): print('{} finish the homework when he is {} at grade{}.'.format(self._name, self.age, self._grade)) self._name="new_name" #error self.age=22 #假如只能根据上面猜测变量名字时候,该变量是不可以改的 self._grade=1 print('after several years\n{} finish the homework when he is {} at grade{}.'.format(self._name, self.age, self._grade)) return if __name__ == "__main__": h1 = HUMAN("xiaoming",15) print(h1.get_name()) print(h1.age) #这里的表现形式,就将age方法变成一个只读属性,注意调用时候不能有() s1 = STUDENT("Hua", 11, 6) s1.do_homework()
5. python 风格的对象;参考link
在python 中定义一个类时候,要考虑到其他函数/对象使用它时候,需要该类能够支持一些操作;
下面只是一些简单的过程,具体使用时候注意
import math class vector: """python 风格类的定义 单下划线 : 只是语言约定,不会直接读取,私有属性 双下划线 : 在实际解释过程,该变量会被改名,并且如果继承过程中,子类与父类取了同样的名字,不会被覆盖;dir 可以用来查看实例的属性 单下划线结尾 """ __slot__ = ('__x','__y','_z') #高速解释器,变量个数;否则的话,会新建一些额外属性;可以有效减少空间占用; def __init__(self,a,b): self.__x = a self.__y = b self._z= a+b @property def x(self): return self.__x @property def y(self): return self.__y def __str__(self): return str(tuple(self)) def __iter__(self): return (i for i in (self.__x, self.__y)) def __repr__(self): """ return vector(__x,__y) """ return (f"{type(self).__name__} ({self.__x},{self.__y})") def __hash__(self): return hash(self.__x) ^ hash(self.__y) def __abs__(self): return math.hypot(self.__x, self.__y) def __bool__(self): return bool(abs(self)) v1 = vector(3,5) for i in v1: #__iter__ print(i) print(v1) #__repr_ print(str(v1)) #__repr_ print(hash(v1)) print(abs(v1)) print(v1._z) #约定为私有变量,但是还是可以读取到的; #print(v1.__x) #经过改名以后,是无法读到的; print(dir(v1))
还有多重继承,操作符重载...
上下文管理器:通过 with expression [as target] 来实现,而expression 中实现 “上下文管理器协议”;
- __enter__: 在进入with语法前调用,返回值会 赋给 with 中的target
- __exit__: 在退出with 语法块时调用,一般用作异常处理;
- 在python中标准库提供了contextlib 模块,将上下文管理器当成一个 装饰器 来使用; 参考link
class TimeRecord: def __init__(self): print("here is __init__") self._start = 0 self._end= 0 def __enter__(self): print("here is __enter__") self._start = time.time() def __exit__(self,exc_type,exc_value,traceback): print("args", exc_type, exc_value, traceback) self._end = time.time() print(f"total time is {self._end - self._start}") def f(n): return 1 if n < 2 else f(n -1) + f(n - 2) with TimeRecord(): print(f(5))
6. 性能优化:
- 使用函数去处理:局部变量操作比全局变量要快
- __slot__
- 缓存: lru_cache (functools) : 空间换时间
from functools import lru_cache class TimeRecord: def __init__(self): self._start = 0 self._end= 0 def __enter__(self): self._start = time.time() def __exit__(self,exc_type,exc_value,traceback): print("args", exc_type, exc_value, traceback) self._end = time.time() print(f"total time is {self._end - self._start}") def fib(n): return 1 if n < 2 else fib(n - 2) + fib(n - 1) @lru_cache def fib_with_cache(n): return 1 if n < 2 else fib_with_cache(n - 1) + fib_with_cache(n - 2) with TimeRecord(): fib(40) with TimeRecord(): fib_with_cache(40)
7. 打包分发工具,可以使用setuptools;