Python 元编程

元编程充分发挥了动态语言的优点,可以用来简化大量重复操作

装饰器

函数装饰器

装饰器就是一个函数,它接受一个函数作为参数并返回一个新的函数

1
2
3
4
5
6
@wrapper
def demo():
pass

# 等价于
demo = wrapper(demo)

使用 functools.wraps 装饰器来保留原函数的元信息
@wraps 有一个重要特征是它能让你通过属性__wrapped__直接访问被包装函数;这个特性可以用来解除函数的装饰器(仅使用只有一个装饰器的情况)

  • 如果装饰器有参数,需要再加一层包装;因为第一层包装的参数是传入的方法func
1
2
# 带参数的装饰器等价于
func = wrapper(args)(func)

类方法/实例方法作为装饰器

与普通装饰器一样,仅在调用的时候存在差异

类作为装饰器

为了将类作为一个装饰器,需要实现__call__() 方法,在 call 方法中完成操作

为类和静态方法提供装饰器

需要在 @classmethod, @staticmethod 之前

使用装饰器为方法增加参数

只需要在最终的 wrapper 层方法中增加参数即可

  • 只能添加强制关键字参数
  • 函数签名中没有增加的参数,需要手动修改回来
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from functools import wraps
import inspect

def optional_debug(func):
if 'debug' in inspect.getargspec(func).args:
raise TypeError('debug argument already defined')

@wraps(func)
def wrapper(*args, debug=False, **kwargs):
if debug:
print('Calling', func.__name__)
return func(*args, **kwargs)

sig = inspect.signature(func)
parms = list(sig.parameters.values())
parms.append(inspect.Parameter('debug',
inspect.Parameter.KEYWORD_ONLY,
default=False))
wrapper.__signature__ = sig.replace(parameters=parms)
return wrapper

使用装饰器扩充类的功能

可以使用装饰器修改类的功能或扩充类的方法;是除了元类和继承之外的第三种方法

元类

使用元类创建类

定义一个元类(继承 type 的类)并实现__call__()方法,可以在 call 方法中生成一个新的类

捕获类的属性定义顺序

元类可以很容易的捕获类的定义信息,在 __new__() 方法中捕获

1
2
3
4
5
6
7
8
9
class Demo(type):
def __new__(cls, clsname, bases, clsdict):
# clsdict 中包含类的定义参数
return type.__new__(cls, clsname, bases, clsdict)


@classmethod
def __prepare__(cls, clsname, bases):
return OrderedDict()

以编程的方式定义类

  • 将类的定义的源代码以字符串的形式定义,接着用 exec() 执行

  • 使用 types.new_class() 方法初始化

    • 需要传入 类名、父类元祖、关键字参数、一个填充类字典的回调函数
    • 回调函数,它是一个用来接受类命名空间的映射对象的函数。 通常这是一个普通的字典,但是它实际上是 __prepare__() 方法返回的任意对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # Methods
    def __init__(self, name, shares, price):
    self.name = name
    self.shares = shares
    self.price = price

    def cost(self):
    return self.shares * self.price
    cls_dict = {
    '__init__' : __init__,
    'cost' : cost,
    }

    # Make a class
    import types

    Stock = types.new_class('Stock', (), {}, lambda ns: ns.update(cls_dict))
    Stock.__module__ = __name__

    types 模块中还有其他的方法,例如仅执行准备方法, types.prepare_class()

  • 直接实例化一个元类来创建类 type(clsname, (), {})

    • 这个方法相对 types.new_class() 它忽略了一些关键的步骤,例如__prepare__()的调用

避免重复的属性方法

demo

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2017-2021 HonQi

请我喝杯咖啡吧~