from functools import wraps, partial import logging def debug(func=None, prefix=''): if func is None: return partial(debug, prefix=prefix) log = logging.getLogger(func.__module__) msg = prefix + func.__name__ @wraps(func) def wrapper(*args, **kwargs): log.debug(msg) print(msg) return func(*args, **kwargs) return wrapper
@debug(prefix='***') def add(x, y): return x + y add(1,3)
***add 4
Debug一个类的所有实例方法
def debugmethods(cls): for name, val in vars(cls).items(): if callable(val): print name, val setattr(cls, name, debug(val)) return cls
@debugmethods class Spam: def grok(self): pass def bar(self): pass def foo(self): pass @classmethod def gun(cls): pass @staticmethod def fire(): pass
grok <function grok at 0x1026c2758> bar <function bar at 0x1026c2140> foo <function foo at 0x1026c2668>
vars(Spam).items() # Spam.__dict__.items()
[('grok', <function __main__.grok>), ('__module__', '__main__'), ('bar', <function __main__.bar>), ('fire', <staticmethod at 0x10ece4b78>), ('gun', <classmethod at 0x10ece4980>), ('foo', <function __main__.foo>), ('__doc__', None)]
以上缺点就是并不能装饰classmethod和staticmethod,为什么呢?从打印来看,你会发现函数地址不在一个scope下
def debugattr(cls): orig_getattribute = cls.__getattribute__ def __getattribute__(self, name): print('Get:', name) return orig_getattribute(self, name) cls.__getattribute__ = __getattribute__ return cls
@debugattr class Point(object):#Python2.7中一定要继承object,否则没有object的一些属性 def __init__(self, x, y): self.x = x self.y = y def __getattr__(self, e): return self.__dict__[e] p = Point(2,3) p.x dir(Point)
('Get:', 'x') ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattr__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
解决办法metaclass
class debugmeta(type): def __new__(cls, clsname, bases, clsdict): clsobj = super(debugmeta, cls).__new__(cls, clsname, bases, clsdict) clsobj = debugmethods(clsobj) return clsobj ''' # python 3 syntax class Base(metaclass=debugmeta): pass ''' class Base(object): __metaclass__ = debugmeta
__metaclass__ <class '__main__.debugmeta'>
isinstance(Point, type) dir(debugmeta)
['__abstractmethods__', '__base__', '__bases__', '__basicsize__', '__call__', '__class__', '__delattr__', '__dict__', '__dictoffset__', '__doc__', '__eq__', '__flags__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__instancecheck__', '__itemsize__', '__le__', '__lt__', '__module__', '__mro__', '__name__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasscheck__', '__subclasses__', '__subclasshook__', '__weakrefoffset__', 'mro']
class Spam(Base): def __init__(self, name): self.name = name def bar(self): print "I'm Spam.bar"
bar <function bar at 0x1026c2c08> __init__ <function __init__ at 0x1026c2c80>
dir(Spam)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__metaclass__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar']
body = ''' def __init__(self, name): self.name = name def bar(self): print "I'm Spam.bar" '''
clsdict = type.__prepare__('Spam', (Base,))
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-14-cf797e00aba3> in <module>() ----> 1 clsdict = type.__prepare__('Spam', (Base,)) AttributeError: type object 'type' has no attribute '__prepare__'
注意,Python 2 中, type 并未实现 __prepare__()
方法。在 Python 3 中, type 实现了 __prepare__()
方法。 Python 解释器构造类的时候,在解析class body 之前,会调用__prepare__()
函数。并且Python 解释器在调用 __prepare__()
之前总是先检查__prepare__()
的存在。 存在就会调用,不存在就返回一个一般的dict。
def prepare_class(name, *bases, metaclass=None, **kwargs): if metaclass is None: metaclass = compute_default_metaclass(bases) prepare = getattr(metaclass, '__prepare__', None) if prepare is not None: return prepare(name, bases, **kwargs) else: return dict()
prepare returns a dictionary-like object which is used to store the class member definitions during evaluation of the class body. In other words, the class body is evaluated as a function block (just like it is now), except that the local variables dictionary is replaced by the dictionary returned from prepare. This dictionary object can be a regular dictionary or a custom mapping type.
__prepare__
函数返回一个类词典对象用来存储在解析class body 期间发现的类成员定义。局部变量词典会被该方法返回的词典替换。参考PEP 3115 -- Metaclasses in Python 3000
class MyInt(int): def __add__(self, other): print "I am a new int:)" return super(MyInt, self).__add__(other) i = MyInt(2) print i+3
I am a new int:) 5
type(i), type(MyInt),type(tuple), type(list), type(dict), type(int), type(type)
(__main__.MyInt, type, type, type, type, type, type)
dir(type)
['__abstractmethods__', '__base__', '__bases__', '__basicsize__', '__call__', '__class__', '__delattr__', '__dict__', '__dictoffset__', '__doc__', '__eq__', '__flags__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__instancecheck__', '__itemsize__', '__le__', '__lt__', '__module__', '__mro__', '__name__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasscheck__', '__subclasses__', '__subclasshook__', '__weakrefoffset__', 'mro']
class InterfaceMeta(type): def __new__(cls, name, parents, dct): # create a class_id if it's not specified if 'class_id' not in dct: dct['class_id'] = name.lower() # open the specified file for writing if 'file' in dct: filename = dct['file'] dct['file'] = open(filename, 'w') # we need to call type.__new__ to complete the initialization return super(InterfaceMeta, cls).__new__(cls, name, parents, dct)
Interface = InterfaceMeta('Interface', (), dict(file='tmp.txt')) print(Interface.class_id) print(Interface.file)
interface <open file 'tmp.txt', mode 'w' at 0x10277c030>
每次都这样构造出一个新类会非常心累,因此和 装饰器一样 Python 又为我们提供了 语法糖。定义一个 有__metaclass__
属性的类,该类的构造自动会使用该 __metaclass__
指向的元类来构造。简化和方便了我们写代码;
class Interface(object): __metaclass__ = InterfaceMeta file = 'tmp.txt' print(Interface.class_id) print(Interface.file)
interface <open file 'tmp.txt', mode 'w' at 0x10277c0c0>
继承的子类也会只要元类未被修改,都会通过该元类来构造出来。
class UserInterface(Interface): file = 'foo.txt' print(UserInterface.file) print(UserInterface.class_id)
<open file 'foo.txt', mode 'w' at 0x10ed490c0> userinterface
Abstract Base Class(ABC)
抽象基类ABC,主要定义了基本类和最基本的抽象方法,为子类提供共有API。这个abc 包下有一个 abc.ABCMeta, 我们已经知道了元类,又知道了ABC是抽象基类,那这个 ABCMeta 就是创建 ABC 抽象基类的元类了。抽象基类一般不实现具体方法。都是定义的抽象方法和属性: @abstractmethod
和 @abstarctproperty
.
抽象基类当然是用来继承的,这里的继承分两种:直接代码上的继承和将其他类注册为虚拟子类的继承方式。区别就是,直接继承,必须要实现抽象方法;而通过注册(register) 是可以不实现抽象方法的,但是还是会被认为是该ABC的子类,可以使用 issubclass 和 issubinstance 验证。
from abc import ABCMeta class MyABC(): __metaclass__ = ABCMeta pass MyABC.register(tuple) assert issubclass(tuple, MyABC) assert isinstance((), MyABC)
以下是 issubclass 和 isinstance 的真正调用函数 __instancecheck__
和 __subclasscheck__
class ABCMeta(type): def __instancecheck__(cls, inst): """Implement isinstance(inst, cls).""" return any(cls.__subclasscheck__(c) for c in {type(inst), inst.__class__}) def __subclasscheck__(cls, sub): """Implement issubclass(sub, cls).""" candidates = cls.__dict__.get("__subclass__", set()) | {cls} return any(c in candidates for c in sub.mro())
更多详细内容可以参考PEP 3119
six(python 2 * 3 = six)
six 其实就是一个 Python 2 和 Python 3 的使用元类写法的兼容库。Python 2 中元类的使用通过 __metaclass__
, Python 3 中是通过参数的形式 Class(Base1, Base2, metaclass=ABCMeta) 完成的。
装饰器是给一个类添加元类的23兼容写法。@six.add_metaclass(metaclass)
@add_metaclass(Meta) class MyClass(object): pass That code produces a class equivalent to class MyClass(object, metaclass=Meta): pass on Python 3 or class MyClass(object): __metaclass__ = MyMeta on Python 2. Note that class decorators require Python 2.6. However, the effect of the decorator can be emulated on Python 2.5 like so: class MyClass(object): pass MyClass = add_metaclass(Meta)(MyClass)
import json json.dumps({'li': None})
'{"li": null}'
l = ['fucl']*256 l[ord('a')]
'fucl'
import datetime datetime.datetime.fromtimestamp(1516877655)
datetime.datetime(2018, 1, 25, 18, 54, 15)
class MyMeta(type): def __new__(meta, name, bases, dct): print '-----------------------------------' print "Allocating memory for class", name print meta print bases print dct return super(MyMeta, meta).__new__(meta, name, bases, dct) def __init__(cls, name, bases, dct): print '-----------------------------------' print "Initializing class", name print cls print bases print dct super(MyMeta, cls).__init__(name, bases, dct) class MyKlass(object): __metaclass__ = MyMeta def foo(self, param): pass bar = 2
----------------------------------- Allocating memory for class MyKlass <class '__main__.MyMeta'> (<type 'object'>,) {'bar': 2, '__module__': '__main__', 'foo': <function foo at 0x10276ce60>, '__metaclass__': <class '__main__.MyMeta'>} ----------------------------------- Initializing class MyKlass <class '__main__.MyKlass'> (<type 'object'>,) {'bar': 2, '__module__': '__main__', 'foo': <function foo at 0x10276ce60>, '__metaclass__': <class '__main__.MyMeta'>}
可以看到,元类是用来操作和生成类的,python 中,类本身也是一种特殊的对象,他是 type 类的实例,通过元类可以改变某些类的新建和初始化行为即类被加载出来的时候的行为和属性。
class MyMeta(type): def __call__(cls, *args, **kwds): print '__call__ of ', str(cls) print '__call__ *args=', str(args) return type.__call__(cls, *args, **kwds) class MyKlass(object): __metaclass__ = MyMeta def __init__(self, a, b): print 'MyKlass object with a=%s, b=%s' % (a, b) print 'gonna create foo now...' foo = MyKlass(1, 2) bar = MyKlass(3,4)
gonna create foo now... __call__ of <class '__main__.MyKlass'> __call__ *args= (1, 2) MyKlass object with a=1, b=2 __call__ of <class '__main__.MyKlass'> __call__ *args= (3, 4) MyKlass object with a=3, b=4
from string import Template Template("$name is $value").substitute(name="hacker", value=10) class _TemplateMetaclass(type): pattern = r""" %(delim)s(?: (?P<escaped>%(delim)s) | # Escape sequence of two delimiters (?P<named>%(id)s) | # delimiter and a Python identifier {(?P<braced>%(id)s)} | # delimiter and a braced identifier (?P<invalid>) # Other ill-formed delimiter exprs ) """ def __init__(cls, name, bases, dct): super(_TemplateMetaclass, cls).__init__(name, bases, dct) if 'pattern' in dct: pattern = cls.pattern else: pattern = _TemplateMetaclass.pattern % { 'delim' : _re.escape(cls.delimiter), 'id' : cls.idpattern, } cls.pattern = _re.compile(pattern, _re.IGNORECASE | _re.VERBOSE) class Template: """A string class for supporting $-substitutions.""" __metaclass__ = _TemplateMetaclass delimiter = '$' idpattern = r'[_a-z][_a-z0-9]*' def __init__(self, template): self.template = template # Search for $$, $identifier, ${identifier}, and any bare $'s def _invalid(self, mo): i = mo.start('invalid') lines = self.template[:i].splitlines(True) if not lines: colno = 1 lineno = 1 else: colno = i - len(''.join(lines[:-1])) lineno = len(lines) raise ValueError('Invalid placeholder in string: line %d, col %d' % (lineno, colno)) def substitute(*args, **kws): if not args: raise TypeError("descriptor 'substitute' of 'Template' object " "needs an argument") self, args = args[0], args[1:] # allow the "self" keyword be passed if len(args) > 1: raise TypeError('Too many positional arguments') if not args: mapping = kws elif kws: mapping = _multimap(kws, args[0]) else: mapping = args[0] # Helper function for .sub() def convert(mo): # Check the most common path first. named = mo.group('named') or mo.group('braced') if named is not None: val = mapping[named] # We use this idiom instead of str() because the latter will # fail if val is a Unicode containing non-ASCII characters. return '%s' % (val,) if mo.group('escaped') is not None: return self.delimiter if mo.group('invalid') is not None: self._invalid(mo) raise ValueError('Unrecognized named group in pattern', self.pattern) return self.pattern.sub(convert, self.template)
'hacker is 10'
比如将某些类的某些公共属性进行提前计算,避免以后每次都需要计算,这里就是 cls.pattern 这个属性,提前生成正则,以后所有使用 Template 的地方就不用再次计算该属性了。