如何在 class 属性定义中使用 class 方法
How can one use a class method in a class attribute definition
一切都在标题中。我想创建一个 class 方法和一个 class 属性,它们都只构造一次,当 class 创建时,使用第二个定义中的第一个。
尽我最大的努力,我得到了 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
来访问 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
s 被实现为一个特殊对象,完全使用 the descriptor protocol when looked up on the class; inside the definition, as a raw name (unqualified), it's a special classmethod
object, not a normal function and it's not bound to the class properly. If you check the pure Python definition of classmethod
, you'll note it's just a normal object that implements __init__
(for construction) and __get__
(for descriptor lookup), but not __call__
, meaning that if you have the raw classmethod
object, it's not actually a callable 进行处理。
诀窍是限定引用,因此 "magic" 恰好将其绑定到 class,并将限定引用移到 class
定义之外(因此 Foo
是一个定义的名称,可以引用进行绑定)更改:
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
,我怀疑您最终可能会对子class 和覆盖 bar
感兴趣。正如所写,您需要在定义每个子 class 之后显式地重新定义 bar_vect
,否则它会使用继承的 bar_vect
,基于 Foo.bar
,即使子class定义了自己的bar
classmethod
。每次显式重新定义 bar_vect
是一种选择,但另一种方法是在 class 重新定义 bar
时使用 metaclasses 隐式定义 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
无缝地使用 classes 对 bar
的最本地定义,可在 class定义时间,因此除非在 class 定义之后重新定义 bar
,否则它始终有效,并且尽可能高效地工作。要使其实时使用 bar
,您需要采取更极端的措施,在每次使用时执行动态查找和(禁止缓存)重建 np.vectorize
对象,这不是最佳选择最少。
为了完整起见,基于动态缓存的解决方案(向 致敬)使用动态填充缓存,增加的开销最小(在我看来更重要的是,抽象出与工作)对于缓存条目已经存在的情况,通过使用 dict
subclass 定义 __missing__
:
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)
Subclasses 不需要(也不应该)重写 bar_vect
(并且不能意外访问 __bar_vect_registry
因为它的名称被破坏了,所以只有由定义的方法Foo
会看到它;将名称更改为 _bar_vect_registry
,一个下划线,如果子 classes 应该可以访问的话),它们只是覆盖 bar
和 Foo
当 bar_vect
首次在子 class(或其实例)上访问时,bar_vect
将 create/cache 向量化访问器。
你真正的问题是你尝试在 class 之前使用 bar
如果完全构建,所以你没有得到预期的对象。
这是一个简化的例子:
class Foo:
@classmethod
def bar(cls, x):
print ('bar called in', cls, 'with', x)
barv = str(bar)
print(str(Foo.bar))
print(Foo.barv)
给出:
<bound method Foo.bar of <class '__main__.Foo'>>
<classmethod object at 0x00000000035B0320>
这表明在class完全构建之前,方法标识符仅绑定到方法定义而不是真正的方法。
如果你想实现你想要的,你必须在 class 定义之外定义 class 变量(在最后一行之后),正如@ShadowRanger
所解释的
您对为什么这不是一个简单的解决方法感到困惑是可以理解的,让我详细说明为什么以这种方式使用 classmethod
行不通...
classmethod
的工作方式是它创建一个描述符,一个在作为对象的属性检索时实现 __get__
的对象。
因此,当您执行 Foo.bar
时,它基本上会加载 bar
class 方法并调用:
bar.__get__(None, Foo)
其中 None
表示实例(有 None 因为它在 class 本身),第二个参数表示 class,一个 class 方法不可调用,因为那样它也没有 class 来绑定它!
不仅如此,绑定它的 class 对象也不存在,直到 class
定义块结束(并且 metaclass type
实际上把它放在一起)所以最低限度是在实际定义 class 之后创建 bar_vect
:
class Foo( object ):
a = 1 #lets use an example that actually uses the class
@classmethod
def bar( cls, x ):
return x+cls.a
Foo.bar_vect = np.vectorize( Foo.bar )
这当然可以,但是你破坏了 subclasses 的功能,如果你想改变 a
怎么办?
class Subfoo(Foo):
a = 3 #this will have no effect on
assert Subfoo.bar_vect(np.array([ 1, 2, 3 ])) == np.array([ 4, 5, 6 ])
#this SHOULD work but doesn't because you bound bar_Vect to just Foo
#subclasses mean nothing to your class method
在这种情况下让它工作的唯一方法是为每个子 np.vectorize
重新创建至少一个 class,最简单的版本是每次调用 bar_vect
:
class Foo( object ):
a = 1
@classmethod
def bar( cls, x ):
return x+cls.a
@classmethod
def bar_vect(cls,arg):
return np.vectorize(cls.bar)(arg)
这显然是不可取的,因为每次使用 x.bar_vect
时它都会调用 np.vectorize
,但是您可以记录所有 classes 并且仅在新的 class 被使用:
_bar_vect_registry = {}
@classmethod
def bar_vect(cls,arg):
try:
return cls._bar_vect_registry[cls](arg)
except KeyError:
cls._bar_vect_registry[cls] = np.vectorize(cls.bar)
return cls._bar_vect_registry[cls](arg)
一切都在标题中。我想创建一个 class 方法和一个 class 属性,它们都只构造一次,当 class 创建时,使用第二个定义中的第一个。
尽我最大的努力,我得到了 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
来访问 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
s 被实现为一个特殊对象,完全使用 the descriptor protocol when looked up on the class; inside the definition, as a raw name (unqualified), it's a special classmethod
object, not a normal function and it's not bound to the class properly. If you check the pure Python definition of classmethod
, you'll note it's just a normal object that implements __init__
(for construction) and __get__
(for descriptor lookup), but not __call__
, meaning that if you have the raw classmethod
object, it's not actually a callable 进行处理。
诀窍是限定引用,因此 "magic" 恰好将其绑定到 class,并将限定引用移到 class
定义之外(因此 Foo
是一个定义的名称,可以引用进行绑定)更改:
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
,我怀疑您最终可能会对子class 和覆盖 bar
感兴趣。正如所写,您需要在定义每个子 class 之后显式地重新定义 bar_vect
,否则它会使用继承的 bar_vect
,基于 Foo.bar
,即使子class定义了自己的bar
classmethod
。每次显式重新定义 bar_vect
是一种选择,但另一种方法是在 class 重新定义 bar
时使用 metaclasses 隐式定义 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
无缝地使用 classes 对 bar
的最本地定义,可在 class定义时间,因此除非在 class 定义之后重新定义 bar
,否则它始终有效,并且尽可能高效地工作。要使其实时使用 bar
,您需要采取更极端的措施,在每次使用时执行动态查找和(禁止缓存)重建 np.vectorize
对象,这不是最佳选择最少。
为了完整起见,基于动态缓存的解决方案(向 dict
subclass 定义 __missing__
:
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)
Subclasses 不需要(也不应该)重写 bar_vect
(并且不能意外访问 __bar_vect_registry
因为它的名称被破坏了,所以只有由定义的方法Foo
会看到它;将名称更改为 _bar_vect_registry
,一个下划线,如果子 classes 应该可以访问的话),它们只是覆盖 bar
和 Foo
当 bar_vect
首次在子 class(或其实例)上访问时,bar_vect
将 create/cache 向量化访问器。
你真正的问题是你尝试在 class 之前使用 bar
如果完全构建,所以你没有得到预期的对象。
这是一个简化的例子:
class Foo:
@classmethod
def bar(cls, x):
print ('bar called in', cls, 'with', x)
barv = str(bar)
print(str(Foo.bar))
print(Foo.barv)
给出:
<bound method Foo.bar of <class '__main__.Foo'>>
<classmethod object at 0x00000000035B0320>
这表明在class完全构建之前,方法标识符仅绑定到方法定义而不是真正的方法。
如果你想实现你想要的,你必须在 class 定义之外定义 class 变量(在最后一行之后),正如@ShadowRanger
所解释的您对为什么这不是一个简单的解决方法感到困惑是可以理解的,让我详细说明为什么以这种方式使用 classmethod
行不通...
classmethod
的工作方式是它创建一个描述符,一个在作为对象的属性检索时实现 __get__
的对象。
因此,当您执行 Foo.bar
时,它基本上会加载 bar
class 方法并调用:
bar.__get__(None, Foo)
其中 None
表示实例(有 None 因为它在 class 本身),第二个参数表示 class,一个 class 方法不可调用,因为那样它也没有 class 来绑定它!
不仅如此,绑定它的 class 对象也不存在,直到 class
定义块结束(并且 metaclass type
实际上把它放在一起)所以最低限度是在实际定义 class 之后创建 bar_vect
:
class Foo( object ):
a = 1 #lets use an example that actually uses the class
@classmethod
def bar( cls, x ):
return x+cls.a
Foo.bar_vect = np.vectorize( Foo.bar )
这当然可以,但是你破坏了 subclasses 的功能,如果你想改变 a
怎么办?
class Subfoo(Foo):
a = 3 #this will have no effect on
assert Subfoo.bar_vect(np.array([ 1, 2, 3 ])) == np.array([ 4, 5, 6 ])
#this SHOULD work but doesn't because you bound bar_Vect to just Foo
#subclasses mean nothing to your class method
在这种情况下让它工作的唯一方法是为每个子 np.vectorize
重新创建至少一个 class,最简单的版本是每次调用 bar_vect
:
class Foo( object ):
a = 1
@classmethod
def bar( cls, x ):
return x+cls.a
@classmethod
def bar_vect(cls,arg):
return np.vectorize(cls.bar)(arg)
这显然是不可取的,因为每次使用 x.bar_vect
时它都会调用 np.vectorize
,但是您可以记录所有 classes 并且仅在新的 class 被使用:
_bar_vect_registry = {}
@classmethod
def bar_vect(cls,arg):
try:
return cls._bar_vect_registry[cls](arg)
except KeyError:
cls._bar_vect_registry[cls] = np.vectorize(cls.bar)
return cls._bar_vect_registry[cls](arg)