元类和自省

2019-12-02  本文已影响0人  yangyuecho

自省和反射

自省 (introspection): 可以在运行时获得对象的类型信息

type introspection is the ability of a program to examine the type or properties of an object at runtime

例子; python 中最常见的 dir

    def __init__(self, val) -> None:
        self.x = val

    def bar(self):
        return self.x

...

>>> dir(Foo(5))
# ['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__',
# '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__',
# '__repr__', '__setattr__', '__str__', '__weakref__', 'bar', 'x']

反射 (reflection) 是包括自省的,指检查,自省以及修改自身结构和行为的能力。

Introspection should not be confused with reflection, which goes a step further and is the ability for a program to manipulate the values, meta-data, properties and/or functions of an object at runtime.
reflection is the ability of a process to examine, introspect, and modify its own structure and behavior.

例子:

# Without reflection
obj = Foo()
obj.hello()

# With reflection
obj = globals()['Foo']()
getattr(obj, 'hello')()

# With eval
eval('Foo().hello()')

元类是什么

python 中,所有的数据类型都可以视为对象,当然也可以自定义对象。

类也是一种对象,类同时拥有创建对象(类实例)的能力。

class myObject(object):
    pass

item = myObject()

print(myObject) # <class '__main__.myObject'>
print(item) # <__main__.myObject object at 0x10a936080>

当使用 class 关键字时,Python 解释器自动创建这个对象。但就和Python 中的大多数事情一样,Python 仍然提供给了手动处理的方法:

type 除了可以告诉你一个对象的类型是什么,还能动态的创建类。

print(type(1))  # <class 'int'>
print(type('1'))    # <class 'str'>
print(type(item))   # <class '__main__.myObject'>
print(type(myObject))   # <class 'type'>

# type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
myMetaClass = type('myMetaClass', (), {})
print(myMetaClass)  # <class '__main__.myMetaClass'>
print(myMetaClass()) # <__main__.myMetaClass object at 0x10a9367f0>

元类就是用来创建类的“东西”,在 Python中,类也是对象,你可以动态的创建类。这就是当你使用关键字 class 时 Python 在幕后做的事情,而这就是通过元类来实现的。

元类的使用

声明元类

class Spam(metaclass=Meta):  # 3.0 and later
class Spam(Eggs, metaclass=Meta):  # Other supers okay

class spam(object): # 2.6 version(only)
    __metaclass__ = Meta  

自定义元类

使用简单的工厂函数

任何可调用对象都可以用作一个元类,只要它接受传递的参数并且返回与目标类兼容的一个对象

# 元类会自动将你通常传给‘type’的参数作为自己的参数传入
def upper_attr(future_class_name, future_class_parents, future_class_attr):
    '''返回一个类对象,将属性都转为大写形式'''
    #  选择所有不以'__'开头的属性
    attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__'))
   # 将它们转为大写形式
    uppercase_attr = dict((name.upper(), value) for name, value in attrs)
 
    # 通过'type'来做类对象的创建
    return type(future_class_name, future_class_parents, uppercase_attr)

class Foo(metaclass=upper_attr):
    bar = 'bip'
    
f = Foo()
print(f.BAR)    # bip

从技术上讲,实例属性查找通常只是搜索实例及其所有类的dict字典;元类不包含在实例查找中。

用常规类重载类创建调用

class UpperAttrMetaclass(type):
    def __new__(cls, name, bases, dct):
        attrs = ((name, value) for name, value in dct.items() if not name.startswith('__'))
        uppercase_attr = dict((name.upper(), value) for name, value in attrs)
#         这种方式其实不是OOP。我们直接调用了type,而且我们没有改写父类的__new__方法。
#         return type(future_class_name, future_class_parents, uppercase_attr)
#         复用type.__new__方法
#         return type.__new__(upperattr_metaclass, future_class_name, future_class_parents, uppercase_attr)
#         如果使用super方法的话,我们还可以使它变得更清晰一些
        return super(UpperAttrMetaclass, cls).__new__(cls, name, bases, uppercase_attr)

class Foo(metaclass=UpperAttrMetaclass):
    bar = 'bip'
    
f = Foo()
print(f.BAR)    # bip

元类其实只做了三件事:

为什么用类而不是函数

由于 metaclass 可以接受任何可调用的对象,那为何还要使用类呢,因为很显然使用类会更加复杂啊?这里有好几个原因:

为什么要使用元类

元类的主要用途是创建API。一个典型的例子是Django ORM。

对于非常简单的类,你可能不希望通过使用元类来对类做修改。你可以通过其他两种技术来修改类:

以下是一个计数功能分别用装饰器和元类实现的例子:

from types import FunctionType


def tracer(func):
    calls = 0
    def on_call(*args, **kwargs):
        nonlocal calls
        calls += 1
        print('call %s to %s' % (calls, func.__name__))
        return func(*args, **kwargs)
    return on_call


class Person1:
    @tracer
    def __init__(self, name, pay):
        self.name = name
        self.pay = pay
        
    @tracer
    def giveRaise(self, percent):
        self.pay *= (1.0 + percent)
        
    @tracer
    def lastName(self):
        return self.name.split()[-1]
    
    
bob = Person1('Bob Smith', 50000)
sue = Person1('Sue Jones', 100000)
print(bob.name, sue.name)
sue.giveRaise(.10)
print(sue.pay)
print(bob.lastName(), sue.lastName())

# call 1 to __init__
# call 2 to __init__
# Bob Smith Sue Jones
# call 1 to giveRaise
# 110000.00000000001
# call 1 to lastName
# call 2 to lastName
# Smith Jones    

class MetaTrace(type):
    def __new__(meta, classname, supers, classdict):
        for attr, attrval in classdict.items():
            if type(attrval) is FunctionType:
                classdict[attr] = tracer(attrval)
        return type.__new__(meta, classname, supers, classdict)

class Person2(metaclass=MetaTrace):
    def __init__(self, name, pay):
        self.name = name
        self.pay = pay

    def giveRaise(self, percent):
        self.pay *= (1.0 + percent)
        
    def lastName(self):
        return self.name.split()[-1]
    
bob = Person2('Bob Smith', 50000)
sue = Person2('Sue Jones', 100000)
print(bob.name, sue.name)
sue.giveRaise(.10)
print(sue.pay)
print(bob.lastName(), sue.lastName())

# call 1 to __init__
# call 2 to __init__
# Bob Smith Sue Jones
# call 1 to giveRaise
# 110000.00000000001
# call 1 to lastName
# call 2 to lastName
# Smith Jones

单例的例子:

# 单例模式
class Singleton(type):
    def __init__(self, *args, **kwargs):
        self.__instance = None
        super().__init__(*args, **kwargs)

    def __call__(self, *args, **kwargs):
        if self.__instance is None:
            self.__instance = super().__call__(*args, **kwargs)
            return self.__instance
        else:
            return self.__instance

# Example
class Spam(metaclass=Singleton):
    def __init__(self):
        print('Creating Spam')
          
# 非元类单例
class _Spam:
    def __init__(self):
        print('Creating Spam')

_spam_instance = None

def Spam():
    global _spam_instance

    if _spam_instance is not None:
        return _spam_instance
    else:
        _spam_instance = _Spam()
        return _spam_instance    

缓存例子:

import weakref

class Cached(type):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.__cache = weakref.WeakValueDictionary()

    def __call__(self, *args):
        if args in self.__cache:
            return self.__cache[args]
        else:
            obj = super().__call__(*args)
            self.__cache[args] = obj
            return obj

# Example
class Spam(metaclass=Cached):
    def __init__(self, name):
        print('Creating Spam({!r})'.format(name))
        self.name = name
上一篇下一篇

猜你喜欢

热点阅读