';classmethod';对象在元类';Python 3中的s方法
我试图实现一个元类,它在创建第一个类及其实例时初始化类变量。我想保留一个新的神奇方法';classmethod';对象在元类';Python 3中的s方法,python,python-3.x,metaprogramming,metaclass,Python,Python 3.x,Metaprogramming,Metaclass,我试图实现一个元类,它在创建第一个类及其实例时初始化类变量。我想保留一个新的神奇方法\uuuuuuuuuuuuuuuuuuuuuuuuuuuu,它应该被称为classmethod(比如\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。所以我实现了如下: class StaticLoad(type): __loaded_classes = set() def __call__(cls, *args, **kwargs): if
\uuuuuuuuuuuuuuuuuuuuuuuuuuuu
,它应该被称为classmethod(比如\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。所以我实现了如下:
class StaticLoad(type):
__loaded_classes = set()
def __call__(cls, *args, **kwargs):
if cls not in cls.__loaded_classes:
if hasattr(cls, '__load__'):
cls.__load__()
cls.__loaded_classes.add(cls)
return super().__call__(*args, **kwargs)
class BaseClass(metaclass=StaticLoad):
s = 0
class MyClass(BaseClass):
@classmethod
def __load__(cls):
print("Loading", cls.__name__, "...")
cls.s += 1
obj1 = MyClass()
obj2 = MyClass()
print(MyClass.s)
它工作正常,并给出正确的结果:
Loading MyClass ...
1
现在我想实现方法\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
(无需每次在上面键入@classmethod
)。我试过这个:
class StaticLoad(type):
__loaded_classes = set()
def __call__(cls, *args, **kwargs):
if cls not in cls.__loaded_classes:
if hasattr(cls, '__load__'):
# I try to apply classmethod routine to make
# cls.__load__ a classmethod
classmethod(cls.__load__)()
cls.__loaded_classes.add(cls)
return super().__call__(*args, **kwargs)
class BaseClass(metaclass=StaticLoad):
s = 0
class MyClass(BaseClass):
# @classmethod line was deleted
def __load__(cls):
print("Loading", cls.__name__, "...")
cls.s += 1
obj1 = MyClass()
obj2 = MyClass()
print(MyClass.s)
我得到了一个错误:
Traceback (most recent call last):
File "example.py", line 22, in <module>
obj1 = MyClass()
File "example.py", line 7, in __call__
classmethod(cls.__load__)()
TypeError: 'classmethod' object is not callable
回溯(最近一次呼叫最后一次):
文件“example.py”,第22行,在
obj1=MyClass()
调用中第7行的文件“example.py”__
类方法
TypeError:“classmethod”对象不可调用
看起来classmethod
例程仅在类定义中正确可用
我应该如何改进元类以使其正常工作?我想保留类BaseClass
和MyClass
的内容,就像我在上面写的那样,在@AnttiHaapala的帮助下,将所有的魔法放入StaticLoad
,解决方案很简单。而不是打电话
classmethod(cls.__load__)()
我不得不打电话
cls.__load__(cls)
在@AnttiHaapala的帮助下,解决方案很简单。而不是打电话
classmethod(cls.__load__)()
我不得不打电话
cls.__load__(cls)
如果要对类创建的某些方法和属性执行转换,可以在元类“\uuuuu new\uuuu
函数上执行转换
因为yu已经有了一个元类,所以您所要做的就是实现它的\uuuuu new\uuuuuuuu
方法来转换classmethod中的任何\uuuuuuuu加载\uuuuuu
方法:
class StaticLoad(type):
__loaded_classes = set()
def __new__(metacls, name, bases, namespace):
if "__load__" in namespace and not isinstance(namespace["__load__"], classmethod):
namespace["__load__"] = classmethod(namespace["load"])
return super().__new__(metacls, name, bases, namespace)
def __call__(cls, *args, **kwargs):
if cls not in cls.__class__.__loaded_classes:
if hasattr(cls, '__load__'):
cls.__load__()
type(cls).__loaded_classes.add(cls)
return super().__call__(*args, **kwargs)
(我做的另一个更改是明确说明“加载的类”应该在元类上访问,而不是在类本身上)。如果您想对类创建的某些方法和属性执行转换,您可以在元类的“\uuuuu new”
函数上执行转换
因为yu已经有了一个元类,所以您所要做的就是实现它的\uuuuu new\uuuuuuuu
方法来转换classmethod中的任何\uuuuuuuu加载\uuuuuu
方法:
class StaticLoad(type):
__loaded_classes = set()
def __new__(metacls, name, bases, namespace):
if "__load__" in namespace and not isinstance(namespace["__load__"], classmethod):
namespace["__load__"] = classmethod(namespace["load"])
return super().__new__(metacls, name, bases, namespace)
def __call__(cls, *args, **kwargs):
if cls not in cls.__class__.__loaded_classes:
if hasattr(cls, '__load__'):
cls.__load__()
type(cls).__loaded_classes.add(cls)
return super().__call__(*args, **kwargs)
(我做的另一个更改是解释“加载的类”应该在元类上访问,而不是在类本身上)。cls.\uu加载()
或cls.\uu加载(cls)
不起作用?!令人惊讶的是,classmethod对象是不可调用的。它的\uuuu get\uuuu
在类或实例上查找时将返回可调用对象。我还没有尝试调用cls.\uu加载(cls)
@AnttiHaapala,将其替换为cls.\uu加载(cls)
帮助。谢谢。cls.\uuuu load\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?!令人惊讶的是,classmethod对象是不可调用的。它的\uuuu get\uuuu
在类或实例上查找时将返回可调用对象。我还没有尝试调用cls.\uu加载(cls)
@AnttiHaapala,将其替换为cls.\uu加载(cls)
帮助。谢谢你。你的“其他更改”不完整。您没有使用cls.\u在if
语句中加载了\u类
。我认为不需要更改,因为双前导下划线将调用属性名称上的名称损坏(使用元类名称作为前缀)。这意味着类不太可能覆盖它。是的。我在那里也换了。我保留了双下划线,但我根本不想使用名称损坏-另一种选择是使用裸\uuuuuu class\uuuuuuuuu
而不是cls.\uuuuuu class\uuuuuuuuu
。这正是名称损坏的设计目的。元类无法预先知道哪些名称可能对其实例(普通类)中的属性有用。当它希望为自己的实现使用属性时,可以使用双前导下划线名称,以减少名称与实例用于其他目的的名称发生意外冲突的可能性。至于cls.\uuuu class\uuuuu
vesustype(cls)
或\uuuu class\uuuu
,我实际上建议更好的选择是显式命名元类:StaticLoad.\uu loaded\u class
。是的,它在这里工作得很好。但这并不是“它设计的确切情况”——这将是简单的继承。从语义上讲,显式处理元类上的属性更有意义。但是是的,没有。\uuuu class\uuuuuuu
组件,Emaging就可以正常工作。您的“其他更改”是不完整的。您没有使用cls.\u在if
语句中加载了\u类
。我认为不需要更改,因为双前导下划线将调用属性名称上的名称损坏(使用元类名称作为前缀)。这意味着类不太可能覆盖它。是的。我在那里也换了。我保留了双下划线,但我根本不想使用名称损坏-另一种选择是使用裸\uuuuuu class\uuuuuuuuu
而不是cls.\uuuuuu class\uuuuuuuuu
。这正是名称损坏的设计目的。元类无法预先知道哪些名称可能对其实例(普通类)中的属性有用。当它希望为自己的实现使用属性时,可以使用双前导下划线名称,以减少名称与实例用于其他目的的名称发生意外冲突的可能性。至于cls.\uuuu class\uuuuu
vesustype(cls)
或\uuuu class\uuuu
,我实际上建议更好的选择是显式命名元类:StaticLoad.\uu loaded\u class
。是的,它在这里工作得很好。但这并不是“它设计的确切情况”——这将是简单的继承。从语义上讲,显式处理元类上的属性更有意义。但是,是的,这种方法是可行的