Python 编程 | 连载 17 - 高阶函数与装饰器

2022-09-26 16:06:44 浏览数 (1)


一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第12天,点击查看活动详情。

一、Python 中类的高阶函数

__str__ 函数,当print当前实例化对象的时候,会打印出该函数中的return的信息,相当于Java中的 toString 函数,也就是对象的描述信息的定义函数

代码语言:javascript复制
class Student():

    def __init__(self, name):
        self.name = name
    # 定义实例化对象的描述信息
    def __str__(self):
        return 'Student[name={}]'.format(self.name)


    def breath(self):
        print('Student can breath')

if __name__ == '__main__':
    stu = Student('子渊')
    print(stu)
    stu.breath()

打印对象时,输出了对象的属性信息也就是在__str__函数中返回的内容

__getattr__ 函数,当调用的属性或者方法不存在时,会返回返函数中定义的信息。

代码语言:javascript复制
class Student():

    def __init__(self, name):
        self.name = name

    def __str__(self):
        return 'Student[name={}]'.format(self.name)

    def __getattr__(self, property):
        print('{}不存在'.format(property))

    def breath(self):
        print('Student can breath')

if __name__ == '__main__':
    stu = Student('子渊')
    print(stu)
    stu.breath()
    stu.age

__setattr__ 函数,可以拦截当前类中不存在的属性或值,给不存在的属性设置属性值;在上面的 Student 类中增加函数

代码语言:javascript复制
def __setattr__(self, key, value):
    if key not in self.__dict__:
        self.__dict__[key] = value
    print('key={}, value={}'.format(key, value))

在main函数下增加代码

代码语言:javascript复制
stu.like = 'lilith'
stu.like

__call__ 函数可以将类变成一个函数;Student 类下增加函数

代码语言:javascript复制
def __call__(self,name):
    print(name)

在main函数中增加代码

代码语言:javascript复制
# 像函数一样调用
stu('stark')

实现链式调用

代码语言:javascript复制
class Hero:

    def __init__(self, attr=''):
        self.__attr = attr

    def __getattr__(self, key):
        if self.__attr:
            key = '{}.{}'.format(self.__attr, key)

        else:
            key = key
        # 一定要返回Hero类才能实现链式调用    
        return Hero(key)

    def __call__(self, name):
        return name


hero = Hero()
name = hero.a.b.c('stark')
print(name)

二、装饰器

装饰器:

  • 是一种函数
  • 可以接受函数作为参数
  • 可以返回函数
  • 接收一个函数作为参数,内部对其进行处理,然后返回一个新函数,动态增强函数功能

定义一个装饰器

代码语言:javascript复制
def out(func): # 外围函数
    def inner(*args, **kwargs): # 内嵌函数,参数为外围函数参数的参数
        return func(*args, **kwargs) 
    return inner # 返回内嵌函数名,不加()

装饰器的使用方式

  • 将被调用的函数直接作为参数参入装饰器的外围函数的参数
  • 将装饰器与被调用函数绑定在一起
  • @符号 装饰器函数放在被调用函数的上一行,被调用的函数正常定义,只需要直接调用被执行函数即可
代码语言:javascript复制
def output_log(func):

    # 定义内嵌函数,用来指定传入的函数,内嵌函数的参数为传入的函数的参数
    def inner(*args, **kwargs):
        print('[info]{}被调用'.format(func.__name__))
        res = func(*args, **kwargs)
        print('[info]{}返回的结果为:{}'.format(func.__name__, res))
        return res

    # 返回内嵌函数的函数名
    return inner


# 使用装饰器
@output_log
def alpha(data):
    return data

res = alpha('pc12138')
print(res)

使用关键字传参和位置传参,并在装饰器中打印出传入的参数

代码语言:javascript复制
def output_log(func):

    # 定义内嵌函数,用来指定传入的函数,内嵌函数的参数为传入的函数的参数
    def inner(*args, **kwargs):
        print('[info]{}被调用'.format(func.__name__))
        print('[info]{}传入的元组类型参数为{}'.format(func.__name__, args))
        print('[info]{}传入的字典类型参数为{}'.format(func.__name__, kwargs))
        res = func(*args, **kwargs)
        print('[info]{}返回的结果为:{}---------'.format(func.__name__, res))

        return res

    # 返回内嵌函数的函数名
    return inner


# 使用装饰器
@output_log
def alpha(data):
    return data

alpha(data='pc12138')
alpha('pc12138')

类中的常用装饰器

@classmethod 装饰器

使用 @classmethod 装饰器标注的函数可以不经过实例化而直接使用类调用

代码语言:javascript复制
@classmethod
def func(cls, args):
    do

cls 表示当前类本身,替代普通类函数中的self,self是指类实例化后的对象本身

代码语言:javascript复制
class Bravo():

    def __init__(self, x):
        self.x = x

    def walk(self):
        print('普通成员函数,需要实例化对象调用')

    @classmethod
    def run(cls):
        print('类直接调用的函数,无须实例化')

Bravo.run()

@classmethod 标注的函数可以通过类直接调用时

代码语言:javascript复制
# 其他代码保持不变
@classmethod
def run(cls):
    print('类直接调用的函数,无须实例化')
    cls.walk()

@classmethod 标注的函数中无法调用普通的类的成员函数

代码语言:javascript复制
class Bravo():

    def __init__(self, x):
        self.x = x

    def walk(self):
        print('普通成员函数,需要实例化对象调用')
        self.run()

    @classmethod
    def run(cls):
        print('类直接调用的函数,无须实例化')
        # cls.walk()

Bravo.run()
bravo = Bravo('bravo')
bravo.walk()

普通类的成员函数中可以调用 @classmethod 标注的函数

代码语言:javascript复制
# 其他代码保持不变

# 实例化对象直接调用
bravo.run()

实例化对象可以调用普通的类成员函数也可以调用 @classmethod 标注的函数

@staticmethod 装饰器

@staticmethod 可以将类函数可以不经过实例化而直接被调用,被装饰器调用的函数无须传递 self 或者 cls 函数,且无法在该函数内调用其他类函数或者类变量

代码语言:javascript复制
class Bravo():

    def __init__(self, x):
        self.x = x

    def walk(self):
        print('普通成员函数,需要实例化对象调用')
        self.run()

    @staticmethod
    def run():
        print('类直接调用的函数,无须实例化,且无须传入cls或者self参数')
        # self.wakl()

    # @classmethod
    # def rush(cls):
    #     print()


Bravo.run()
bravo = Bravo('bravo')
bravo.walk()
bravo.run()

类和普通对象以及类的普通函数中都可以调用 @staticmethod 标注的函数

代码语言:javascript复制
@staticmethod
def run():
    print('类直接调用的函数,无须实例化')
    self.walk()

@staticmethod 标注的函数中不能调用类的普通函数

增加一个 @classmethod 标注的函数

代码语言:javascript复制
@classmethod
def rush(cls):
    print('类直接调用的函数,无须实例化,需要传入cls参数')
    cls.run()

@classmethod 标注的函数中可以调用 @staticmethod 标注的函数

代码语言:javascript复制
@staticmethod
def run():
    print('类直接调用的函数,无须实例化,且无须传入cls或者self参数')
    self.rush()

@staticmethod 标注的函数中无法调用 @classmethod 标注的函数

  • 类的普通函数中既可以调用@staticmethod标注的函数也可以调用@classmethod标注的函数,因为含有self参数
  • @classmethod标注的函数中可以调用@staticmethod标注的函数,因为有cls参数,但是不能调用普通类的函数,因为需要实例化
  • @staticmethod标注的函数不可以调用@staticmethod标注的函数和类的普通函数,因为没有参数

@property 装饰器

@property 可以将类函数的执行免去括弧,类似于调用属性的方式

代码语言:javascript复制
class Bravo():

    def __init__(self, x):
        self.x = x

    def walk(self):
        print('普通成员函数,需要实例化对象调用')

    @property
    def run(self):
        print('类似于普通类函数,需要传入self参数,不同的是调用时不必加括弧')

bravo = Bravo('bravo')
bravo.run

带有参数的 @property 标注的函数

代码语言:javascript复制
class Bravo():
    # 其余代码不变
    
    @property
    def rush(self):
        return '带参数的@property函数要通过@函数名.setter设置参数,传入的参数为{}'.format(self.__name)

    # rush函数参数设置方式
    @rush.setter
    def rush(self, name):
        self.__name = name

bravo = Bravo('bravo')
bravo.rush = 'Stark'
res = bravo.rush
print(res)

@property 标注的函数如果有参数,要通过 @函数名.setter 来设置参数

0 人点赞