python元编程

  1. Meta Class的理解
    django:
    https://docs.djangoproject.com/en/dev/topics/db/models/#meta-options

  2. 元编程
    https://zhuanlan.zhihu.com/p/29849145
    https://zhuanlan.zhihu.com/p/40916705
    https://segmentfault.com/a/1190000023764901 # 非常好的文章

  3. 基础知识【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"})



  1. 进阶理解
  • 装饰器的核心思想,就是装饰【函数这个对象】、让函数自身代码不变的情况下、增添一些具有普适性的功能。
  • 元类的核心思想,就是捣鼓【类这个对象】、使你能对其有着最高程度的控制权。
  • 【重点】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
'''

  1. 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])

原文地址:https://www.cnblogs.com/amize/p/14599983.html