python元编程
u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd') # 这个相当于动态创建了一个类
u.save()
-
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 # 非常好的文章
https://www.liaoxuefeng.com/wiki/1016959663602400/1017592449371072 # 大佬
基础的基础
## 1 type创建class
Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
## 2 先定义metaclass,就可以创建类,最后创建实例。
# metaclass是类的模板,所以必须从`type`类型派生:
class ListMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['add'] = lambda self, value: self.append(value)
return type.__new__(cls, name, bases, attrs)
class MyList(list, metaclass=ListMetaclass):
pass
L = MyList()
L.add(1)
print(L)
- 基础知识【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])
- 更新以及理解 20210901【prepare】【with_metaclass】
https://stackoverflow.com/questions/18513821/python-metaclass-understanding-the-with-metaclass
https://www.liaoxuefeng.com/wiki/1016959663602400/1017592449371072
# 1. 元类
class DictMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['append'] = cls.dict_append
return type.__new__(cls, name, bases, attrs)
def dict_append(self, another_dict):
return {**self, **another_dict}
# 2. python3 创建类
class MyDict(dict, metaclass=DictMetaclass):
pass
# 2. 兼容python2和3的创建类
def with_metaclass(meta, *bases):
class metaclass(type):
def __new__(cls, name, this_bases, d):
return meta(name, bases, d)
return type.__new__(metaclass, 'temporary_class', (), {})
class MyDict2(with_metaclass(DictMetaclass, dict)):
pass
# 3. 创建实例
master_node = {'10.101.35.171': ['int4', 'nokiaSR123$']} # 部署实际ip
agent_dict = {
'10.101.35.172': ['int4', 'nokiaSR123$'],
# '10.101.35.246': ['int4', 'nokiaSR123$'],
}
new = MyDict(master_node).append(agent_dict)
new2 = MyDict2(master_node).append(agent_dict)
print(new)
print(new2)
# __prepare__的理解
class member_table(dict):
def __init__(self):
self.member_names = []
def __setitem__(self, key, value):
if key not in self:
self.member_names.append(key)
dict.__setitem__(self, key, value)
class OrderedClass(type):
@classmethod
def __prepare__(metacls, name, bases):
classdict = member_table()
print("prepare return dict id is:", id(classdict))
return classdict
def __new__(metacls, name, bases, classdict):
print("new get dict id is:", id(classdict))
result = type.__new__(metacls, name, bases, dict(classdict))
result.member_names = classdict.member_names
print("the class's __dict__ id is:", id(result.__dict__))
return result
def __init__(cls, name, bases, classdict):
print("init get dict id is ", id(classdict))
super().__init__(name, bases, classdict)
class MyClass(metaclass=OrderedClass):
def method1(self):
pass
def method2(self):
pass
print("MyClass locals() id is ", id(locals()))
print(MyClass.member_names)
MyClass.attr1 = 'attr1'
print(MyClass.__dict__)
print(id(MyClass.__dict__))
print(MyClass.member_names)
prepare(创建命名空间)-> 依次执行类定义语句 -> new(创建类)-> init(初始化类)
元类定义了prepare以后,会最先执行prepare方法,返回一个空的定制的字典,然后再执行类的语句,类中定义的各种属性被收集入定制的字典,最后传给new和init方法。
再来看其它输出:
上面的例子,在new方法中,dict被替换成一个普通的dict。所以MyClass.member_names不会记录class创建以后新增的属性。同时__dict__属性是类命名空间的一个代理,每次查看其id都不同。
3.6版本以前,prepare方法主要用来返回一个orderdict对象,以保存类中属性的添加顺序。而3.6版本以后,默认已经是保持顺序的了。
浙公网安备 33010602011771号