robot 源码解读6【元类和描述符类】

  1. robot中比较难理解的【元类和描述符类】
  • 【重点1】with_metaclass,initnew
  • 【重点2】getset

with_metaclass见如下文章:


import copy


class setter(object):   # 将类中的方法变成类的属性【属性名:_setter__方法名】

    def __init__(self, method):
        self.method = method
        self.attr_name = '_setter__' + method.__name__
        self.__doc__ = method.__doc__

    def __get__(self, instance, owner):
        if instance is None:
            return self
        try:
            return getattr(instance, self.attr_name)
        except AttributeError:
            raise AttributeError(self.method.__name__)

    def __set__(self, instance, value):
        if instance is None:
            return
        setattr(instance, self.attr_name, self.method(instance, value))


def with_metaclass(meta, *bases):   # 虚拟元类,参数元类meta,返回元类metaclass 【更正20210421】返回的不是元类,正常的类见【相当于 1,2】
    """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(name, bases, attr)

    print(type.__new__(metaclass, 'temporary_class', (), {}))
    return type.__new__(metaclass, 'temporary_class', (), {})  # return __new__的情况 , 返回【类】,【参数:当前准备创建的类的对象,类的名字,类继承的父类集合,类的方法集合】


class SetterAwareType(type):    # 元类 ,将被setter修饰的属性添加到__slots__

    def __new__(cls, name, bases, dct):
        slots = dct.get('__slots__')
        if slots is not None:
            for item in dct.values():
                if isinstance(item, setter):
                    slots.append(item.attr_name)
        return type.__new__(cls, name, bases, dct)

    def test1(self):
        print('test1 ok')


# 不是元类了,定义Message的父类,copy相关的函数
class ModelObject(with_metaclass(SetterAwareType, object)):  # 还是元类? 不是元类20210420, 不明白为什么要with_metaclass(SetterAwareType, object)弄一下

    def copy(self, **attributes):
        copied = copy.copy(self)
        for name in attributes:
            setattr(copied, name, attributes[name])
        return copied

    def test2(self):
        print('test2 ok')


# 相当于 1  【重点】函数方法的话可以灵活修改base类
class temporary_class2(object, metaclass=SetterAwareType):
    pass

# 相当于 2  【重点】这种写法类名写死了
class ModelObject2(temporary_class2):

    def copy(self, **attributes):
        copied = copy.copy(self)
        for name in attributes:
            setattr(copied, name, attributes[name])
        return copied

    def test2(self):
        print('test2 ok')

# 相当于 3
class Message2(ModelObject2):

    def test3(self):
        print('test3 ok')

# 相当于 4
# m2 = Message2()
# m2.test3()

class Message(ModelObject):
    """A message created during the test execution.

    Can be a log message triggered by a keyword, or a warning or an error
    that occurred during parsing or test execution.
    """

    @setter
    def parent(self, parent):
        if parent and parent is not getattr(self, 'parent', None):
            self._sort_key = getattr(parent, '_child_sort_key', -1)
        return parent

    def test3(self):
        print('test3 ok')


m = Message()        # <class '__main__.temporary_class'>
# m.test1()    # 错误 AttributeError: 'Message' object has no attribute 'test1'
m.test2()            # test2 ok
m.test3()            # test3 ok

print(SetterAwareType)       # <class '__main__.SetterAwareType'>
print(type(SetterAwareType)) # <class 'type'>
print(ModelObject)           # <class '__main__.ModelObject'>
print(type(ModelObject))     # 1. <class '__main__.SetterAwareType'>
print(Message)               # <class '__main__.Message'>
print(type(Message))         # 1. <class '__main__.SetterAwareType'>
print(m)                     # <__main__.Message object at 0x000001BFAB6D1108>

'''
<class '__main__.temporary_class'>
test2 ok
test3 ok
<class '__main__.SetterAwareType'>
<class 'type'>
<class '__main__.ModelObject'>
<class '__main__.SetterAwareType'>
<class '__main__.Message'>
<class '__main__.SetterAwareType'>
<__main__.Message object at 0x000001BFAB6D1108>
'''

  1. Store 类也有【with_metaclass】 20210624
https://stackoverflow.com/questions/18513821/python-metaclass-understanding-the-with-metaclass

from six import with_metaclass

class Store(with_metaclass(ABCMeta, object)):
    """ Store abstraction.

    Every store implementation must inherit from this class, in order to be
    accepted by LookupHandler.
    """

    @abstractmethod
    def __len__(self):
        raise NotImplementedError

    @abstractmethod
    def add(self, instance, alias):
        """ Remember instance by given alias. """
        raise NotImplementedError

    @abstractmethod
    def get(self, alias):
        """ Return instance remembered by given alias. """
        raise NotImplementedError

    @abstractmethod
    def remove(self, alias):
        """ Forget given alias. """
        raise NotImplementedError

    @abstractmethod
    def remove_object(self, instance):
        """ Forget every alias that this instance is remembered by. """
        raise NotImplementedError

    @abstractmethod
    def reset(self):
        """ Forget everything and revert to starting state. """


class AliasStore(Store):
    """ Basic lookup store implementation.




# 理解
from six import with_metaclass

class Meta(type):
    pass

class Base(object):
    pass

class MyClass(with_metaclass(Meta, Base)):
    pass


# 也可以这样
import six

@six.add_metaclass(Meta)
class MyClass(Base):
    pass

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