Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/358.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
';classmethod';对象在元类';Python 3中的s方法_Python_Python 3.x_Metaprogramming_Metaclass - Fatal编程技术网

';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
vesus
type(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
vesus
type(cls)
\uuuu class\uuuu
,我实际上建议更好的选择是显式命名元类:
StaticLoad.\uu loaded\u class
。是的,它在这里工作得很好。但这并不是“它设计的确切情况”——这将是简单的继承。从语义上讲,显式处理元类上的属性更有意义。但是,是的,这种方法是可行的