python metaclass_彻底理解python3 metaclass

A metaclass is a class whose instances are classes. Like an "ordinary" class defines the behavior of instancs of the class,

a metaclass defines the behavior of classes and their instances.

metaclass的实例是class, 就像普通的class定义其instance一样.

There are numberours use cases for metaclasses. Just to name a few:

* logging and profiling 日志和解析

* interface checking 接口检查

* registering classes at creation time

* automatically adding new methods 自动添加方法

* automatically propery creation 自动创建属性

* proxies 代理

* automatic resource locking/synchronization 自动资源锁和同步

metaclass内置魔力方法研究(magic method)

Principially, metaclasses are defined like any other Python class, but they are classes that inherit from type.

原则上,metaclass定义想普通类一样, 但他们继承自type.

美化输出函数,主要用于打印函数内部参数和内部变量.以下演示都将用到此函数

from tabulate import tabulate #自动输出markdown格式表格

import pandas as pd

def pprint(name, **kwargs):

print('****')

print('*', '```' + name + '```', 'is called\n')

df = pd.DataFrame(kwargs.items(), columns=['param', 'value'])

print(tabulate(df, tablefmt='pipe', headers='keys', showindex=False))

print('****')

__init__ 和__new__

class LittleMeta(type):

def __init__(cls, clsname, superclasses, attributedict):

"""定义一个由此元类创建的class时触发此__init__,

相当于给class增加类属性(class的attribute),就像

class A:

age = 18

"""

cls.age = 18

pprint('__init__', **locals())

def __new__(cls, clsname, superclasses, attributedict):

attributedict['name'] = clsname + '$tom'

pprint('__new__', **locals())

return type.__new__(cls, clsname, superclasses, attributedict)

# 定义一个继承自str类, 并由LittleMeta创建的类

# 使用LittleMeta创建类的两种方法

#方法1

class A(str, metaclass=LittleMeta):

pass

# 方法2

B = LittleMeta('B', (str,), {})

定义A类时 __new__ is called

param

value

cls

clsname

A

superclasses

(,)

attributedict

{'module': 'main', 'qualname': 'A', 'name': 'A$tom'}

首先LittleMeta的__new__被调用, 第一个参数是LittleMeta自己,这里的attributedict是A.__dict__, attributedict['name'] = clsname + '$tom'给类A增加

了一个name属性.

定义class A时元类的 __init__ 被调用

param

value

cls

clsname

A

superclasses

(,)

attributedict

{'module': 'main', 'qualname': 'A', 'name': 'A$tom'}

这里发现attributedict里面没有age, 是因为只有元类的__new__才能直接通过设置A的attributedict的方式给A增加属性,在元类的__init__ 通过cls.attributename = value的方式增加类属性, 也就是说在类A定义之后增加到A.__dict__

可以通过print(A.__dict__) 观察

函数式调用LittleMeta创建B时__new__ is called

param

value

cls

clsname

B

superclasses

(,)

attributedict

{'name': 'B$tom'}

函数式调用LittleMeta创建B时__init__ is called

param

value

cls

clsname

B

superclasses

(,)

attributedict

{'name': 'B$tom'}

__call__

class LittleMeta(type):

def __init__(cls, clsname, superclasses, attributedict):

pprint('__init__', **locals())

def __new__(cls, clsname, superclasses, attributedict):

attributedict['name'] = clsname + '$tom'

pprint('__new__', **locals())

return type.__new__(cls, clsname, superclasses, attributedict)

def __call__(cls, *args, **kwargs):

"""when the instance of LittleMeta is called, in other words,

class is instantiate, e.g. a = A(),

this magic method __call__ is called, bind `run` method to cls:A

当元类的实例被调用, 也就是classA被实例化的时候触发.

这里当class A被实例化的时候增加一个run方法.联想class的```__call__```使得其

实例可以被调用.

"""

cls.run = lambda self, x: print(f'{self} run {x} meters')

pprint('__call__', **locals())

return super().__call__(*args, **kwargs)

class S:

pass

class A(S, metaclass=LittleMeta):

pass

print(A.__dict__)

a = A() # trigger LittleMeta's __call__, 此时才给A绑定一个run方法

a.run(88)

print(A.__dict__)

__new__ is called

param

value

cls

clsname

A

superclasses

(,)

attributedict

{'module': 'main', 'qualname': 'A', 'name': 'A$tom'}

__init__ is called

param

value

cls

clsname

A

superclasses

(,)

attributedict

{'module': 'main', 'qualname': 'A', 'name': 'A$tom'}

{'module': 'main', 'name': 'A$tom', 'doc': None}

__call__ is called

param

value

cls

args

()

kwargs

{}

class

run 88 meters

{'module': 'main', 'name': 'A$tom', 'doc': None, 'run': . at 0x7f41d4ef2ae8>}

此时新增了一个run方法

Creating instance cache using metaclass(使用元类实现实例缓存)

class InstCache(type):

_instances = {} #元类属性

def __call__(cls, *args, **kwargs):

_kw = dict(sorted(kwargs.items()))

_key = (*args, *_kw.values())

if _key not in cls._instances:

cls._instances[_key] = super().__call__(*args, **kwargs)

# cls._instances[_key] = type.__call__(cls, *args, **kwargs)

pprint('__call__', **locals())

return cls._instances[_key]

class S(metaclass=InstCache):

def __init__(self, name, age=18):

self.name = name

self.age = age

x = S('tom', 19)

y = S('tom', age=19)

print(x, y, x is y)

__call__ is called

param

value

cls

args

('tom', 19)

kwargs

{}

_kw

{}

_key

('tom', 19)

class

__call__ is called

param

value

cls

args

('tom',)

kwargs

{'age': 19}

_kw

{'age': 19}

_key

('tom', 19)

class

True

Creating instance cache using inhrit(继承实现实例缓存)

class InstCache(object):

_instances = {}

def __new__(cls, *args, **kwargs):

""""实例创建时触发

"""

_kw = dict(sorted(kwargs.items()))

_key = (*args, *_kw.values())

if _key not in cls._instances:

cls._instances[_key] = super().__new__(cls)

# cls._instances[_key] = object.__new__(cls)

return cls._instances[_key]

class S(InstCache):

def __init__(self, name, age=18):

self.name = name

self.age = age

x = S('tom', 19)

y = S('tom', age=19)

print(x is y)

output

True

只要实例参数的值一致

The "count calls" metaclass 类方法调用次数的元类

from functools import wraps

class FuncCalls(type):

@staticmethod

def call_counter(func):

@wraps(func)

def wrapper(*args, **kwargs):

wrapper.calls += 1

return func(*args, **kwargs)

wrapper.calls = 0

return wrapper

def __new__(cls, clsname, superclasses, attributedict):

for attr, value in attributedict.items():

if callable(value) and not attr.startswith('__'):

value = cls.call_counter(value) #相当于装饰器

attributedict[attr] = value

return super().__new__(cls, clsname, superclasses, attributedict)

class A(metaclass=FuncCalls):

def foo(self):

pass

a = A()

a.foo()

print(a.foo.calls)

a.foo()

print(a.foo.calls)

# output

1

2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值