• 在 Python 中,函数是第一类对象


    函数在 Python 中是第一类对象,这意味着:

    函数是对象--它们可以被引用,传递到一个变量,也可以从其他函数返回。 函数可以被定义在另一个函数中--一个内部函数--也可以作为参数传递给另一个函数。

    def say_name():
        print("Guido van Rossum")


    def say_nationality():
        print("Netherlands")


    def say(func):
        return func


    say(say_name)()
    say(say_nationality)()
    Output:

    Guido van Rossum
    Netherlands
    • 1

    这里我们将两个不同的函数引用(不是括号内的)作为参数发送给say函数,该函数再次返回引用。

    这就是内部函数的工作方式。

    def say():

        def say_name():
            print("Guido van Rossum")

        def say_nationality():
            print("Netherlands")

        say_name()
        say_nationality()

    say()
    Output:

    Guido van Rossum
    Netherlands
    • 1

    装饰器

    你已经看到函数就像Python中的其他对象一样,现在让我们举个例子来看看Python装饰器的魔力。


    def say(func):

        def employer():
            print("说说你的事。")

        def say_name():
            print("我的名字是Guido van Rossum。")

        def say_nationality():
            print("我来自荷兰。")

        def wrapper():
             employer() #雇主()
            say_name()
            say_nationality()
            func()

        return wrapper

    @say
    def start_interview():
        print("真正的面试开始了...")

    start_interview()
    • 1

    输出:

    说些关于你的事情。
    我的名字是Guido van Rossum。
    我来自荷兰。
    真正的面试开始了...

    • 1

    这里当我们调用start_interview方法时。它进入装饰函数say,定义了雇主、say_name、say_nationality和包装函数,最后它返回包装函数的引用,并在调用者函数start_interview的地方调用它。

    用参数装饰函数

    我们还可以装饰一个带参数的函数。我们可以在封装函数中使用*args**kwargs接受这些参数。


    def say(func):

        def wrapper(*args, **kwargs):
            func(*args, **kwargs)
        return wrapper

    @say
    def greet(name):
        print("Hello {}".format(name))

    greet("Goutom")
    输出:

    Hello Goutom
    • 1

    从装饰过的函数返回 当你的装饰函数返回一些东西时会发生什么?就是从包装器中返回调用者函数。让我们看一个例子来理解这一点。

    def my_decorator(func):

        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)
        return wrapper

    @my_decorator
    def doubled(number):
        return number*2

    print(doubled(10))
    Output:

    20
    • 1

    自检

    在Python中,自省是指一个对象在运行时了解其自身属性的能力。例如,一个函数知道它自己的名字和文档。让我们打印 doubled 函数的名字。

    print(doubled.__name__)
    Output:

    wrapper
    • 1

    但它应该是被加倍的。在被装饰后,我们的双倍函数对其身份产生了混淆,因为它是从 wrapper 函数中被调用的。为了解决这个问题,装饰器应该在wrapper函数中使用@functools.wraps装饰器,这将保留原函数的信息。在添加这个装饰器后,它将加倍返回其原始名称。

    一个现实世界的例子

    让我们写一个装饰器,它将计算函数的执行时间(以秒为单位)并在控制台中打印出来。

    import functools
    import time


    def timer(func):

        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            start_time = time.perf_counter()
            value = func(*args, **kwargs)
            end_time = time.perf_counter()
            run_time = end_time - start_time
            print("Finished {} in {} secs".format(repr(func.__name__), round(run_time, 3)))
            return value

        return wrapper
    Now let's use this decorator:

    @timer
    def doubled_and_add(num):
        res = sum([i*2 for i in range(num)])
        print("Result : {}".format(res))

    doubled_and_add(100000)
    doubled_and_add(1000000)
    Output:

    Result : 9999900000
    Finished ‘doubled_and_add’ in 0.0119 secs
    Result : 999999000000
    Finished ‘doubled_and_add’ in 0.0897 secs
    • 1

    现在我们来使用这个装饰器。

    注册插件

    装饰器不一定要包装它们所装饰的函数。他们也可以简单地注册一个函数的存在,并返回它的非包装。例如,这可以用来创建一个轻量级的插件架构。

    PLUGINS = dict()

    def register(func):
        PLUGINS[func.__name__] = func
        return func

    @register
    def add(a, b):
        return a+b

    @register
    def multiply(a, b):
        return a*b

    def operation(func_name, a, b):
        func = PLUGINS[func_name]
        return func(a, b)

    print(PLUGINS)
    print(operation('add', 2, 3))
    print(operation('multiply', 2, 3))
    Output :

    {‘add’: <function add at 0x7fb27f7a8620>, ‘multiply’: <function multiply at 0x7fb27f7a88c8>}
    5
    6
    • 1

    @register装饰器只是在全局PLUGINS dict中存储了一个对被装饰函数的引用。注意,在这个例子中,你不需要写一个内部函数或使用@functools.wraps,因为你返回的是未经修改的原始函数。

    这种简单的插件架构的主要好处是,你不需要维护一个存在哪些插件的列表。这个列表是在插件自己注册时创建的。这使得添加一个新的插件变得非常简单:只需定义函数并使用@register来装饰它。

    装饰类

    有两种不同的方法可以在类上使用装饰器。第一种是通过装饰类的方法或装饰整个类。

    几个内置的类装饰器

    一些内置在 Python 中的常用装饰器是 @classmethod, @staticmethod, 和 @property。@classmethod 和 @staticmethod 装饰器用于定义类命名空间内的方法,这些方法与该类的特定实例没有关系。@property装饰器用于定制类属性的getters和setters。展开下面的方框,可以看到一个使用这些装饰器的例子。你可以在这里了解更多关于内置装饰器的信息。

    让我们看一个例子。

    class Circle:
        def __init__(self, radius):
            self._radius = radius

        @property
        def radius(self):
            ""获取半径的值""
            return self._radius

        @radius.setter
        def radius(self, value):
            ""设置半径,如果是负值,则引发错误"""
            if value >=0:
                self._radius = value
            else:
                raise ValueError("
    半径必须为正")

        @property
        def area(self):
            "
    "计算圆内的面积""
            return self.pi() * self.radius**2

        def cylinder_volume(self, height):
            "
    "计算以圆为底的圆柱体的体积""
            return self.面积 * 高度

        @classmethod
        def unit_circle(cls):
            "
    "创建半径为1的圆的工厂方法""
            return cls(1)

        @classmethod
        def pi():
            "
    "π的值,不过可以用math.pi代替""
            return 3.1415926535
    • 1

    在这个类中:

    .cylinder_volume()是一个常规方法。

    .radius是一个可变的属性:它可以被设置成不同的值。

    然而,通过定义一个setter方法,我们可以做一些错误测试以确保它不会被设置为一个无意义的负数。属性作为属性被访问,没有括号。

    .area是一个不可变的属性:没有.setter()方法的属性不能被改变。即使它被定义为一个方法,它也可以作为一个属性被检索,不需要括号。

    .unit_circle()是一个类方法。它不与Circle的一个特定实例绑定。类方法经常被用作工厂方法,可以创建该类的特定实例。

    .pi()是一个静态方法。它并不真正依赖于Circle类,除了它是其命名空间的一部分。静态方法可以在一个实例或类上调用。

    在控制台中测试。


    >> c = Circle(5)
    >>> c.radius
    5

    >> c.面积
    78.5398163375

    >> c.半径=2
    >> c.面积
    12.566370614

    >>> c.面积=100
    AttributeError: 不能设置属性

    >> c.圆柱体_体积(高度=4)
    50.265482456

    >> c.radius = -1
    ValueError。半径必须是正数

    >> c = Circle.unit_circle()
    >> c.radius
    1

    >> c.pi()
    3.1415926535

    >>> Circle.pi()
    3.1415926535
    • 1

    装饰一个类方法

    这里我们使用之前创建的定时器装饰器。


    class Calculator:

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

        @timer
        def doubled_and_add(self):
            res = sum([i * 2 for i in range(self.num)])
            print("Result : {}".format(res))

    c = Calculator(10000)
    c.doubled_and_add()
    Output:

    Result : 99990000
    Finished 'doubled_and_add' in 0.001 secs
    Decorate a class
    @timer
    class Calculator:

        def __init__(self, num):
            self.num = num
            import time
            time.sleep(2)

        def doubled_and_add(self):
            res = sum([i * 2 for i in range(self.num)])
            print("Result : {}".format(res))

    c = Calculator(100)
    Output:

    Finished 'Calculator' in 2.001 secs
    • 1

    装饰一个类并不能装饰它的方法。在这里,@timer只测量实例化类的时间。

    嵌套装饰器

    def hello(func):
        def wrapper():
            print("Hello")
            func()
        return wrapper

    def welcome(func):

        def wrapper():
            print("Welcome")
            func()
        return wrapper

    @hello
    @welcome
    def say():
        print("Greeting Dome")

    say()
    Output:

    Hello
    Welcome
    Greeting Dome
    • 1

    把这看成是装饰器按照它们列出的顺序被执行。换句话说,@hello调用@welcome,后者调用say()

    带有参数的装饰器

    这里repeat处理装饰器的参数。


    def repeat(*args_, **kwargs_):

        def inner_function(func):

            @functools.wraps(func)
            def wrapper(*args, **kwargs):
                for _ in range(args_[0]):
                    func(*args, **kwargs)
            return wrapper

        return inner_function


    @repeat(4)
    def say(name):
        print(f "Hello {name}")

    say("World")
    • 1

    输出:


    Hello World
    Hello World
    Hello World
    Hello World
    • 1

    你好,世界 你好,世界 你好,世界 你好,世界

    有状态的装饰器

    我们可以使用一个装饰器来跟踪状态。作为一个简单的例子,我们将创建一个装饰器来计算一个函数被调用的次数。


    def count_calls(func):

        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            wrapper.num_calls += 1
            print(f"Call {wrapper.num_calls} of {func.__name__!r}")
            return func(*args, **kwargs)

        wrapper.num_calls = 0
        return wrapper


    @count_calls
    def say():
        print("Hello!")

    say()
    say()
    say()
    say()
    print(say.num_calls)
    • 1

    输出:


    Call 1 of 'say'
    Hello!
    Call 2 of 'say'
    Hello!
    Call 3 of 'say'
    Hello!
    Call 4 of 'say'
    Hello!
    4
    • 1

    对'say'的调用1

    你好!

    调用'say'的第2次

    你好!

    调用'say'的3

    你好!

    呼叫4的 "说"。

    你好!

    4

    对该函数的调用次数存储在包装函数的函数属性num_calls中。

    作为装饰器的类

    维护状态的最好方法是使用类。如果我们想使用类作为装饰器,它需要在其 .init() 方法中把 func 作为一个参数。此外,这个类需要是可调用的,这样它就可以代替被装饰的函数。为了使一个类可以被调用,我们实现了特殊的 .call() 方法。


    class CountCalls。
        def __init__(self, func):
            functools.update_wrapper(self, func)
            self.func = func
            self.num_calls = 0

        def __call__(self, *args, **kwargs):
            self.num_calls += 1
            print(f "调用{self.num_calls}的{self.func.__name__!r}")
            return self.func(*args, **kwargs)


    @CountCalls
    def say():
        print("Hello!")

    say()
    say()
    say()
    say()
    print(say.num_calls)
    • 1

    输出过程:

    对'say'的调用1 你好! 调用'say'的第2次 你好! 调用'say'的3 你好! 呼叫4的 "说"。 你好! 4

    带参数的基于类的装饰器


    class CountCalls:
        def __init__(self, func):
            functools.update_wrapper(self, func)
            self.func = func
            self.num_calls = 0

        def __call__(self, *args, **kwargs):
            self.num_calls += 1
            print(f"Call {self.num_calls} of {self.func.__name__!r}")
            return self.func(*args, **kwargs)


    @CountCalls
    def say():
        print("Hello!")

    say()
    say()
    say()
    say()
    print(say.num_calls)
    Output:

    Call 1 of 'say'
    Hello!
    Call 2 of 'say'
    Hello!
    Call 3 of 'say'
    Hello!
    Call 4 of 'say'
    Hello!
    4
    Class-Based Decorators with Arguments
    class ClassDecorator(object):

        def __init__(self, arg1, arg2):
            print("Arguements of decorator %s, %s" % (arg1, arg2))
            self.arg1 = arg1
            self.arg2 = arg2

        def __call__(self, func):
            functools.update_wrapper(self, func)

            def wrapper(*args, **kwargs):
                return func(*args, **kwargs)
            return wrapper

    @ClassDecorator("arg1""arg2")
    def print_args(*args):
        for arg in args:
            print(arg)

    print_args(1, 2, 3)
    Output:

    Arguements of decorator arg1, arg2
    1
    2
    3
    • 1

    几个实例扩展和基础用法总结。

    本文由 mdnice 多平台发布

  • 相关阅读:
    C语言 数组作为函数参数
    dbeaver怎么批量执行sql
    现货黄金如何拜师学艺?
    Go语学习笔记 - gorm使用 - 表增删改查 | Web框架Gin(八)
    浅析linux内核网络协议栈--linux bridge
    今天告诉你界面控件DevExpress WinForms为何弃用经典视觉样式
    一级造价工程师(安装)- 计量笔记 - 重点必考考点
    Go函数全景:从基础到高阶的深度探索
    Axure8.0教程:身高标尺(动态面板)
    cos和obs腾讯云,和华为云的区别
  • 原文地址:https://blog.csdn.net/qq_40523298/article/details/127556212