python - 如何在类属性定义中使用类方法

标签 python python-2.7 python-2.x

一切都在标题中。我想创建一个类方法和一个类属性,它们都只在创建类时构造一次,使用第二个定义中的第一个。

尽我最大的努力,我得到了一个 TypeError: 'classmethod' object is not callable

这是我的代码:

import numpy as np

class Foo( object ) :

    @classmethod
    def bar( cls, x ) :
        return x+1

    bar_vect = np.vectorize( bar )

Foo.bar_vect( np.array([ 1, 2, 3 ]) )

>> TypeError: 'classmethod' object is not callable

编辑 1:

'classmethod' object is not callable是引发相同错误的问题,但有很多解决方法。我的问题旨在开门见山,清楚地了解如何在没有范围的情况下使用 @classmethod 来访问 cls

我做的另一个尝试如下:

import numpy as np

class Foo( object ) :

    @classmethod
    def bar( cls, x ) :
        return x+1

    bar_vect = np.vectorize( bar )

>> NameError: name 'Foo' is not defined

最佳答案

@classmethod 被实现为使用 the descriptor protocol 处理的特殊对象当抬头看课时;在定义中,作为一个原始名称(非限定),它是一个特殊的 classmethod 对象,而不是一个普通的函数,并且它没有正确地绑定(bind)到类。如果您检查 pure Python definition of classmethod ,你会注意到它只是一个普通对象,它实现了 __init__(用于构造)和 __get__(用于描述符查找),但没有实现 __call__,这意味着如果你有原始的 classmethod 对象,它实际上不是 callable完全没有。

诀窍是限定引用,这样“魔法”恰好将它绑定(bind)到类,并将限定引用移到 class 定义之外(所以 Foo 是一个已定义的名称,可以为绑定(bind)引用)更改:

class Foo(object):
    ... rest of class ...
    bar_vect = np.vectorize(bar)  # Indented and unqualified, BAD

到:

class Foo(object):
    ... rest of class ...
# Must qualify both bar_vect and bar, since no longer in class definition
Foo.bar_vect = np.vectorize(Foo.bar)  # Dedented, so Foo is defined for referencing, GOOD

请注意,由于您正在使用 classmethod,我怀疑您最终可能会对子类化和覆盖 bar 感兴趣。正如所写,您需要在定义每个子类后显式地重新定义 bar_vect,或者它会使用继承的 bar_vect,基于 Foo.bar ,即使子类定义了自己的 bar classmethod。每次显式重新定义 bar_vect 是一种选择,但另一种方法是在类重新定义 bar 时使用元类隐式定义 bar_vect:

class BarVectorized(type):
    def __new__(cls, name, bases, namespace, **kwargs):
        newcls = type.__new__(cls, name, bases, dict(namespace))
        # Make vectorized wrapper for this class (must use new wrapper
        # even if bar unchanged, so cls in bar is correct for lookup of
        # other class attributes/methods)
        try:
            newcls.bar_vect = np.vectorize(newcls.bar)
        except AttributeError:
            pass  # Allow class w/o bar; remove try/except if class must have bar
        return newcls

class Foo(object):
    __metaclass__ = BarVectorized
    @classmethod
    def bar(cls, x): return x + 1

class Foo2(Foo):
    ADD = 2  # Hardcoded 1 is dumb, use class attribute instead!
    @classmethod
    def bar(cls, x):
        return x + cls.ADD

class Foo3(Foo2):
    ADD = 3  # Provide new class attr to change Foo2.bar behavior when called via Foo3

>>> Foo.bar_vect([1,2,3])
array([2, 3, 4])
>>> Foo2.bar_vect([1,2,3])
array([3, 4, 5])
>>> Foo3.bar_vect([1,2,3])
array([4, 5, 6])

根本不需要显式定义 bar_vect,并且 bar_vect 无缝地使用类定义时可用的大多数本地类的 bar 定义,所以除非 bar 在类定义之后被重新定义,否则它总是有效,并且尽可能高效地工作。要使其实时使用 bar,您需要采取更极端的措施,对每个对象执行动态查找和(禁止缓存)重建 np.vectorize 对象使用,至少可以说是次优的。

为了完整起见,一个基于动态缓存的解决方案(向 Tadhg McDonald-Jensen's answer 致敬)使用动态填充的缓存来增加最小的开销(在我看来更重要的是,抽象出与工作无关的样板代码)用于通过使用定义 __missing__dict 子类已经存在缓存条目的情况:

import operator
import numpy as np

class ClassAttrRegistry(dict):
    '''Dictionary keyed by classes which returns optionally wrapped cached attributes'''
    __slots__ = '_wrapper', '_attrgetter'
    def __init__(self, attr, wrapperfunc=lambda x: x):
        self._wrapper = wrapperfunc
        self._attrgetter = operator.attrgetter(attr)
    def __missing__(self, cls):
        self[cls] = wrapped = self._wrapper(self._attrgetter(cls))
        return wrapped

class Foo(object):
    @classmethod
    def bar(cls, x):
        return x + 1

    # Dunder prefix makes cache private to Foo methods; if subclass overrides bar_vect,
    # assumed it's more complex than "vectorized bar"; cache should not be used
    __bar_vect_registry = ClassAttrRegistry('bar', np.vectorize)
    @classmethod
    def bar_vect(cls, x):
        # Get cached vectorized bar (creating if needed) then call it
        return cls.__bar_vect_registry[cls](x)

子类不需要(也不应该)覆盖 bar_vect(并且不会意外访问 __bar_vect_registry,因为它的名称被破坏了,所以只有 定义的方法>Foo 会看到它;将名称更改为 _bar_vect_registry,一个下划线,如果子类可以访问它),它们只是覆盖 barFoo bar_vect 将在首次访问子类(或其实例)上的 bar_vect 时创建/缓存矢量化访问器。

关于python - 如何在类属性定义中使用类方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37569798/

相关文章:

python - str.format() 具有两种对齐方式 { :>} and variable interpolation

python - __slots__ 和 pickle 的奇怪行为

python - 如何根据其他列的值从 pandas 数据框中提取数据?

python - 将正数/负数四舍五入到最接近的 "whole"数字

python - 艰难地学习 Python 示例 5

python - 在 python 中实现数据日志的派生

python - 在 Python 中优雅地检查 -1

python - 在 virtualenv 中使用 pip install 时出现 UnicodeDecodeError

python - 如何找到路径列表中的公共(public)路径?

python - Beautiful Soup 和字符编码