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;

 

posted on 2022-06-26 22:32  学海一扁舟  阅读(23)  评论(0编辑  收藏  举报