-
Meta Class的理解
django:
https://docs.djangoproject.com/en/dev/topics/db/models/#meta-options -
元编程
https://zhuanlan.zhihu.com/p/29849145
https://zhuanlan.zhihu.com/p/40916705
https://segmentfault.com/a/1190000023764901 # 非常好的文章 -
基础知识【new】【type】
# 1. __new__ 方法可以用在下面二种情况
# 1.1 继承不可变数据类型时需要用到
class Inch(float):
"Convert from inch to meter"
def __new__(cls, arg=0.0): # 注意这里的第一个参数
return float.__new__(cls, arg*0.0254) # 变成传给float的第一个参数
print(Inch(12))
# 0.3048
# 1.1 补充 【__new__返回值】float.__new__,和 object.__new__,或者是源码的type.__new__ 返回的都是【类】
class Foo(object):
price = 50
def __new__(cls, *agrs, **kwds):
inst = object.__new__(cls, *agrs, **kwds)
print(inst) # <__main__.Foo object at 0x0000000002E229C8>
return inst # 【类】
def how_much_of_book(self, n):
print(self) # <__main__.Foo object at 0x0000000002E229C8>
return self.price * n
foo = Foo()
print(foo.how_much_of_book(8))
# __new__参数 【参数:当前准备创建的类的对象,类的名字,类继承的父类集合,类的方法集合】
# 1.2 用在元类(python2的例子)
class MetaClass(type):
def __new__(meta, name, bases, dct): #【重点】【meta:类自己】,【name:真正的要实例化的类(Myclass)】,【bases:继承的基类(元祖类型,可以有多个,看另外一个例子)】,【dct:】
print '-----------------------------------
print "Allocating memory for class", name # Myclass
print meta # MetaClass
print bases
print dct
return super(MetaClass, meta).__new__(meta, name, bases, dct)
def __init__(cls, name, bases, dct): #【重点】
print '-----------------------------------'
print "Initializing class", name # Myclass
print cls # Myclass
print bases
print dct
super(MetaClass, cls).__init__(name, bases, dct)
class Myclass(object):
__metaclass__ = MetaClass
def foo(self, param):
print param
p = Myclass()
p.foo("hello")
# -----------------------------------
# Allocating memory for class Myclass
# <class '__main__.MetaClass'>
# (<type 'object'>,)
# {'__module__': '__main__', 'foo': <function foo at 0x1007f6500>, '__metaclass__': <class '__main__.MetaClass'>}
# -----------------------------------
# Initializing class Myclass
# <class '__main__.Myclass'>
# (<type 'object'>,)
# {'__module__': '__main__', 'foo': <function foo at 0x1007f6500>, '__metaclass__': <class '__main__.MetaClass'>}
# hello
# 2. 使用type函数可以创建【类】
# 2.1 基础
# 2.1.1 自动使用class关键字创建一个类
class Student1(object):
pass
# 参数 【参数:类名称,继承的类,类的属性和方法】
# 2.1.2 使用type函数手动创建一个类
Student2 = type("Student2",(),{})
s1 = Student1() #同样都可以创建实例
s2 = Student2() #同样都可以创建实例
print(type(Student1),type(Student2))
print(type(s1),type(s2))
#结果
'''
<class 'type'> <class 'type'>
<class '__main__.Student1'> <class '__main__.Student2'>
'''
# 2.2 深入
# 2.2.1.使用type创建带有属性的类,添加的属性是【类属性】,并不是实例属性
Girl = type("Girl",(),{"country":"china","sex":"male"})
# 2.2.2.使用type创建带有方法的类
#python中方法有普通方法,类方法,静态方法。
def speak(self): # 要带有参数self,因为类中方法默认带self参数。
print("这是给类添加的普通方法")
@classmethod
def c_run(cls):
print("这是给类添加的类方法")
@staticmethod
def s_eat():
print("这是给类添加的静态方法")
#创建类,给类添加静态方法,类方法,普通方法。跟添加类属性差不多.
Boy = type("Boy",(),{"speak":speak,"c_run":c_run,"s_eat":s_eat,"sex":"female"})
- 进阶理解
- 装饰器的核心思想,就是装饰【函数这个对象】、让函数自身代码不变的情况下、增添一些具有普适性的功能。
- 元类的核心思想,就是捣鼓【类这个对象】、使你能对其有着最高程度的控制权。
- 【重点】type是内置的元类,Mixture(type)是自定义的元类【所以参数要符合type类的格式】
# 1. 引子
# metaclass是类的模板,所以必须从`type`类型派生:
class ListMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['add'] = lambda self, value: self.append(value) #【巧妙:append是list的方法】
return type.__new__(cls, name, bases, attrs)
# 有了ListMetaclass,我们在定义类的时候还要指示使用ListMetaclass来定制类,传入关键字参数metaclass:
class MyList(list, metaclass=ListMetaclass):
pass
# 当我们传入关键字参数metaclass时,魔术就生效了,它指示Python解释器在创建MyList时,要通过ListMetaclass.__new__()来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义。
# 测试一下MyList是否可以调用add()方法,而普通的list没有add()方法:
>>> L = MyList()
>>> L.add(1)
>> L
[1]
# 动态修改有什么意义?直接在MyList定义中写上add()方法不是更简单吗?正常情况下,确实应该直接写,通过metaclass修改纯属变态。但是,总会遇到需要通过metaclass修改类定义的。
# 2. 需求
'''
什么叫做最高程度的控制权呢?一个比较简单的栗子就是实现如下需求:
定义一个“人”(Person)类: 它有三个方法:吃饭、睡觉、续几秒
定义 Person 的三个子类: “小张”(Zhang)、“小王”(Wang)、“小江”(Jiang)
定义“人”的子类“小红”(Hong), 要求他:
吃饭像小张一样快
睡觉像小王一样香
续秒像小江一样熟练
你会怎么去实现呢?
如果再要求你把上面三个要求换一换顺序呢?
'''
class Person:
def __init__(self):
self.ability = 1
def eat(self):
print("Person Eat: ", self.ability)
def sleep(self):
print("Person Sleep: ", self.ability)
def save_life(self):
print("Person+ ", self.ability, " s")
class Wang(Person):
def eat(self):
print("Wang Eat: ", self.ability * 2)
class Zhang(Person):
def sleep(self):
print("Zhang Sleep: ", self.ability * 2)
class Jiang(Person):
def save_life(self):
print("Jiang+ inf s")
class Mixture(type):
def __new__(mcs, *args, **kwargs): #【重点】为什么是__new__,因为后面的class Hong(Wang, 是在__new__中传入函数的
name, bases, attr = args[:3] # class Hong(Wang, Zhang, Jiang, metaclass=Mixture):
print(args) # ('Hong', (<class '__main__.Wang'>, <class '__main__.Zhang'>, <class '__main__.Jiang'>), {'__module__': '__main__', '__qualname__': 'Hong'})
person1, person2, person3 = bases
def eat(self):
person1.eat(self)
def sleep(self):
person2.sleep(self)
def save_life(self):
person3.save_life(self)
# 这种写法不好看
# attr["eat"] = eat
# attr["sleep"] = sleep
# attr["save_life"] = save_life
# print(locals().items())
for key, value in locals().items():
if str(value).find("function") >= 0:
attr[key] = value
return type(name, bases, attr) #【重点】type理解 ,对应后面的 【class Hong(Wang, Zhang, Jiang, metaclass=Mixture)】
def test(person):
person.eat()
person.sleep()
person.save_life()
# 顺序1,带metaclass
class Hong(Wang, Zhang, Jiang, metaclass=Mixture):
pass
print('*'*20)
test(Hong())
# 顺序2,带metaclass
class Hong(Zhang, Wang, Jiang, metaclass=Mixture):
pass
print('*'*20)
test(Hong())
# 顺序1,不带
class Hong(Wang, Zhang, Jiang): # 继承,用最近的
pass
print('*'*20)
test(Hong())
# 顺序2,不带
class Hong(Zhang, Wang, Jiang): # 继承,用最近的
pass
print('*'*20)
test(Hong())
#结果
'''
('Hong', (<class '__main__.Wang'>, <class '__main__.Zhang'>, <class '__main__.Jiang'>), {'__module__': '__main__', '__qualname__': 'Hong'})
********************
Wang Eat: 2
Zhang Sleep: 2
Jiang+ inf s
('Hong', (<class '__main__.Zhang'>, <class '__main__.Wang'>, <class '__main__.Jiang'>), {'__module__': '__main__', '__qualname__': 'Hong'})
********************
Person Eat: 1
Person Sleep: 1
Jiang+ inf s
********************
Wang Eat: 2
Zhang Sleep: 2
Jiang+ inf s
********************
Wang Eat: 2
Zhang Sleep: 2
Jiang+ inf s
'''
- robot源码中
# 1. 引子
# 不用class Hong(Wang, Zhang, Jiang, metaclass=Mixture)中的metaclass=Mixture 参数
# type 类创建对象方法1
class SetterAwareType(type): # 元类
def __new__(cls, *args):
print('in SetterAwareType __new__')
return type.__new__(cls, *args)
def __init__(cls, *args):
print('in SetterAwareType __init__')
return type.__init__(cls, *args) # TypeError: type.__init__() takes 1 or 3 arguments
class ModelObject(SetterAwareType):
def test(self):
print('in ModelObject copy')
a = ModelObject('name',(object,),{})
a.test()
# type 类创建对象方法2
class SetterAwareType(type): # 元类
def __new__(cls, *args):
print('in SetterAwareType __new__')
return type.__new__(cls, 'name',(object,), {}) # 'name' 直接传入了
def __init__(cls, *args):
print('in SetterAwareType __init__')
return super(SetterAwareType, cls).__init__(cls, *args)
# return type.__init__(cls, *args) # TypeError: type.__init__() takes 1 or 3 arguments
class ModelObject(SetterAwareType):
def test(self):
print('in ModelObject copy')
a = ModelObject()
a.test()
# 2. 源码
def with_metaclass(meta, *bases):
"""Create a base class with a metaclass."""
# This requires a bit of explanation: the basic idea is to make a
# dummy metaclass for one level of class instantiation that replaces
# itself with the actual metaclass.
class metaclass(type):
def __new__(cls, name, this_bases, d):
return meta(name, bases, d)
return type.__new__(metaclass, 'temporary_class', (), {})
@py2to3
class ModelObject(with_metaclass(SetterAwareType, object)):
__slots__ = []
def copy(self, **attributes):
"""Return shallow copy of this object.
:param attributes: Attributes to be set for the returned copy
automatically. For example, ``test.copy(name='New name')``.
See also :meth:`deepcopy`. The difference between these two is the same
as with the standard ``copy.copy`` and ``copy.deepcopy`` functions
that these methods also use internally.
New in Robot Framework 3.0.1.
"""
copied = copy.copy(self)
for name in attributes:
setattr(copied, name, attributes[name])
return copied
def deepcopy(self, **attributes):
"""Return deep copy of this object.
:param attributes: Attributes to be set for the returned copy
automatically. For example, ``test.deepcopy(name='New name')``.
See also :meth:`copy`. The difference between these two is the same
as with the standard ``copy.copy`` and ``copy.deepcopy`` functions
that these methods also use internally.
New in Robot Framework 3.0.1.
"""
copied = copy.deepcopy(self)
for name in attributes:
setattr(copied, name, attributes[name])
return copied
def __unicode__(self):
return self.name
def __repr__(self):
return repr(str(self))
def __setstate__(self, state):
"""Customize attribute updating when using the `copy` module.
This may not be needed in the future if we fix the mess we have with
different timeout types.
"""
# We have __slots__ so state is always a two-tuple.
# Refer to: https://www.python.org/dev/peps/pep-0307
dictstate, slotstate = state
if dictstate is not None:
self.__dict__.update(dictstate)
for name in slotstate:
# If attribute is defined in __slots__ and overridden by @setter
# (this is the case at least with 'timeout' of 'running.TestCase')
# we must not set the "real" attribute value because that would run
# the setter method and that would recreate the object when it
# should not. With timeouts recreating object using the object
# itself would also totally fail.
setter_name = '_setter__' + name
if setter_name not in slotstate:
setattr(self, name, slotstate[name])