装饰器可以命名它创建的类吗?

Mar*_*man 5 python

我有一个装饰器,它包装函数并生成类(出于比以下示例更好的原因)。这一切都有效,除了我希望能够设置type().

例如,

>>> class Animal:
...     pass
... 
>>> def linnean_name(genus, species):
...     def _linnean_name(fn):
...         class _Animal(Animal):
...             binomial_name = (genus, species)
...             def converse(self):
...                 fn()
...         _Animal.__name__ = fn.__name__.title()
...         _Animal.__module__ = fn.__module__
...         return _Animal
...     return _linnean_name
... 
>>> @linnean_name('Vombatus', 'ursinus')
... def Wombat():
...     print("Hello, I am a wombat.")
... 
>>> sheila = Wombat()
>>> sheila.binomial_name
('Vombatus', 'ursinus')
>>> sheila.converse()
Hello, I am a wombat.
Run Code Online (Sandbox Code Playgroud)

一切都很好,但是

>>> type(sheila)
<class '__main__.linnean_name.<locals>._linnean_name.<locals>._Animal'>
Run Code Online (Sandbox Code Playgroud)

我希望看到的地方

<class '__main__.Wombat'>
Run Code Online (Sandbox Code Playgroud)

这就是原因

...         _Animal.__name__ = fn.__name__.title()
...         _Animal.__module__ = fn.__module__
Run Code Online (Sandbox Code Playgroud)

这似乎没有做任何特别有用的事情。我怎样才能做到这一点?

Atl*_*435 1

您正在寻找__new__. 它允许您指定新的类名并共享预定义行为的公共基类。您还可以通过定义更多基/基类等来创建 MixIn 类。

class Animal:

    def talk(self):
        print(f"Hello, I am {self}")

    def __str__(self):
        return f'{self.family} {self. spec}'

    def __new__(cls, family, spec,  *args, **kwargs):
        cls_name = family.title() + spec.title()
        bases = (cls,)
        typ = type(cls_name, bases, {})
        obj = object.__new__(typ)
        #from here it's like __init__
        #except you return the object instead of None
        #obj is equal to self in the __init__ here

        obj.family = family
        obj.spec = spec
        return obj
    pass


sheila = Animal('Vombatus', 'ursinus')
sheila.talk()
Run Code Online (Sandbox Code Playgroud)

正如评论中提到的,您实际上不必使用类,__new__您只需拥有一个使用type. 然而,在我看来__new__更具可读性。

class Animal:

    def talk(self):
        print(f"Hello, I am {self}")

    def __str__(self):
        return f'{self.family} {self. spec}'
    pass

def factory(family, spec, *args, **kwargs):
    cls_name = family.title() + spec.title()
    bases = (Animal,)
    typ = type(cls_name, bases, {})
    obj = typ(*args, **kwargs)
    obj.family = family
    obj.spec = spec
    return obj


sheila = factory('Vombatus', 'ursinus')
print(type(sheila))
sheila.talk()
Run Code Online (Sandbox Code Playgroud)