为什么类方法在实例化时对普通方法视而不见?
Why are classmethods blind to normal methods when instantiated?
问题
- 为什么 class 方法在实例化时对普通方法视而不见?
- 有没有办法在实例化时通过
self
允许class方法参见普通方法?
观察
未实例化class方法
- 使用class方法时,任何属性的修改都将保留。
- 我希望每次使用 class 函数只是临时存储属性,而 class 方法 运行
实例化class方法
- 类方法不能使用从普通方法设置的任何变量
- 即使 class 被实例化,class 方法仍然完全看不到在普通方法中完成的任何事情!
- 这是意外行为。我希望 class 方法在实例化后表现得像普通方法一样!
示例代码
def fmt_atts(self, att):
"""A simple formatter"""
if hasattr(self, att): print '\t self.{:15s} = {}'.format(att, getattr(self, att))
else: print "\t self.{:15s} does not exist".format(att)
def _report_vals(func):
"""This just reports the current state of values"""
@wraps(func)
def wrapper(*args, **kwargs):
self = args[0]
print '> calling func: {} :: {}'.format(func.__name__, func.__doc__)
res = func(*args, **kwargs)
for att in self.all_attrs:
fmt_atts(self, att)
print '> end func: {}'.format(func.__name__)
return res
return wrapper
class Example(object):
all_attrs = ['att_class', 'att_clsFnSet', 'att_initSet']
att_class = 'set_in_class'
@_report_vals
def __init__(self):
"""setting an attribute and calling self.test2"""
self.att_initSet = 'set_in_init'
self.set_atts_in_classmethod()
@classmethod
@_report_vals
def set_atts_in_classmethod(cls):
"""Sets attributes from within a classmethod"""
cls.att_class = 'set_in_classmethod'
cls.att_clsFnSet = 'set_in_classmethod'
@classmethod
@_report_vals
def view_atts_from_classmethod(cls):
"""View attributes from a classmethod"""
pass
@_report_vals
def view_atts_from_method(self):
"""View attributes from a normal method"""
pass
@_report_vals
def set_atts_in_method(self):
"""Sets attributes from within a normal method"""
self.att_class = 'set_in_method'
self.att_clsFnSet = 'set_in_method'
if __name__ == '__main__':
print '__without init'
print '> calling `Example.att_class` directly'
fmt_atts(Example, 'att_class')
print '# comment: notice that `A.att_class` has a persisting value'
Example.set_atts_in_classmethod()
Example.view_atts_from_classmethod()
print '\n__ post init: ex = Example()'
ex = Example()
print '# comment: notice that `self.att_initSet` has been set but not accessible from classmethod'
ex.set_atts_in_classmethod()
ex.view_atts_from_classmethod()
print '# comment: notice that `self.att_initSet` was set in __init__ but not avialable!'
print '# comment: however, `self.att_class` was set in another classmethod but *IS* accessible'
ex.view_atts_from_method()
print '# comment: notice that `self.att_initSet` is accessible from a normal method'
ex.set_atts_in_method()
ex.view_atts_from_classmethod()
print '# comment: It appears that classmethods can only access attributes set by other classmethods'
print '# comment: even when instanciated'
输出示例
__without init
> calling `Example.att_class` directly
self.att_class = set_in_class
# comment: notice that `A.att_class` has a persisting value
> calling func: set_atts_in_classmethod :: Sets attributes from within a classmethod
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet does not exist
> end func: set_atts_in_classmethod
> calling func: view_atts_from_classmethod :: View attributes from a classmethod
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet does not exist
> end func: view_atts_from_classmethod
__ post init: ex = Example()
> calling func: __init__ :: setting an attribute and calling self.test2
> calling func: set_atts_in_classmethod :: Sets attributes from within a classmethod
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet does not exist
> end func: set_atts_in_classmethod
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet = set_in_init
> end func: __init__
# comment: notice that `self.att_initSet` has been set but not accessible from classmethod
> calling func: set_atts_in_classmethod :: Sets attributes from within a classmethod
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet does not exist
> end func: set_atts_in_classmethod
> calling func: view_atts_from_classmethod :: View attributes from a classmethod
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet does not exist
> end func: view_atts_from_classmethod
# comment: notice that `self.att_initSet` was set in __init__ but not avialable!
# comment: however, `self.att_class` was set in another classmethod but *IS* accessible
> calling func: view_atts_from_method :: View attributes from a normal method
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet = set_in_init
> end func: view_atts_from_method
# comment: notice that `self.att_initSet` is accessible from a normal method
> calling func: set_atts_in_method :: Sets attributes from within a normal method
self.att_class = set_in_method
self.att_clsFnSet = set_in_method
self.att_initSet = set_in_init
> end func: set_atts_in_method
> calling func: view_atts_from_classmethod :: View attributes from a classmethod
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet does not exist
> end func: view_atts_from_classmethod
# comment: It appears that classmethods can only access attributes set by other classmethods
# comment: even when instantiated
为什么 class 方法在实例化时对普通方法视而不见?
Class(或静态)方法并不意味着作用于实例。它们是 class 的所有实例 共有的方法。因此,class 方法作用于一个特定实例是不合逻辑的。
这些通常是实用功能。例如,Vector
class 可以将 dot_product
实现为 class 方法,而不是实例方法。这是有道理的,因为这样的操作涉及多个实例。
有没有办法传递 self 以允许 class方法在实例化时看到正常方法?
是的,有,虽然它没有真正意义。
首先,class 方法 能够 查看实例方法。例如:
class Foo:
def speak(self):
print("Hello")
@classmethod
def make_speak(cls, instance):
instance.speak()
foo = Foo()
Foo.make_speak(foo)
输出:
Hello
让我们更进一步,稍微改变一下 make_speak
方法:
@classmethod
def make_speak(cls):
print(cls.speak)
Foo.make_speak()
输出:
<function Foo.speak at 0x000001E58331EF28>
此输出显示,cls.speak
引用的 speak
方法实际上对应于尚未附加到任何实例的 speak
方法。调用该函数时可以看得更清楚:
@classmethod
def make_speak(cls):
cls.speak()
Foo.make_speak()
输出:
TypeError: speak() missing 1 required positional argument: 'self'
引发TypeError
,因为Foo
class 中的speak
方法需要一个未传递的参数。
解决方法
免责声明:以下不是一个好的解决方案。
speak
方法需要一个实例作为参数才能成为 运行。那么,就给它吧:
@classmethod
def make_speak(cls, instance):
cls.speak(instance)
foo = Foo()
Foo.make_speak(foo)
输出:
Hello
但是通过这样做,您希望make_speak
方法作用于特定的Foo
实例,从而调用它的speak
方法。
这就相当于直接调用foo.speak
.
这个例子可能看起来很明显而且乏味。然而,它强调了一个事实,即如果一个 class 方法只接受一个实例作为参数(可能还有其他不是这个 class 实例的东西),那么它 等价于一个实例方法.
问题
- 为什么 class 方法在实例化时对普通方法视而不见?
- 有没有办法在实例化时通过
self
允许class方法参见普通方法?
观察
未实例化class方法
- 使用class方法时,任何属性的修改都将保留。
- 我希望每次使用 class 函数只是临时存储属性,而 class 方法 运行
实例化class方法
- 类方法不能使用从普通方法设置的任何变量
- 即使 class 被实例化,class 方法仍然完全看不到在普通方法中完成的任何事情!
- 这是意外行为。我希望 class 方法在实例化后表现得像普通方法一样!
示例代码
def fmt_atts(self, att):
"""A simple formatter"""
if hasattr(self, att): print '\t self.{:15s} = {}'.format(att, getattr(self, att))
else: print "\t self.{:15s} does not exist".format(att)
def _report_vals(func):
"""This just reports the current state of values"""
@wraps(func)
def wrapper(*args, **kwargs):
self = args[0]
print '> calling func: {} :: {}'.format(func.__name__, func.__doc__)
res = func(*args, **kwargs)
for att in self.all_attrs:
fmt_atts(self, att)
print '> end func: {}'.format(func.__name__)
return res
return wrapper
class Example(object):
all_attrs = ['att_class', 'att_clsFnSet', 'att_initSet']
att_class = 'set_in_class'
@_report_vals
def __init__(self):
"""setting an attribute and calling self.test2"""
self.att_initSet = 'set_in_init'
self.set_atts_in_classmethod()
@classmethod
@_report_vals
def set_atts_in_classmethod(cls):
"""Sets attributes from within a classmethod"""
cls.att_class = 'set_in_classmethod'
cls.att_clsFnSet = 'set_in_classmethod'
@classmethod
@_report_vals
def view_atts_from_classmethod(cls):
"""View attributes from a classmethod"""
pass
@_report_vals
def view_atts_from_method(self):
"""View attributes from a normal method"""
pass
@_report_vals
def set_atts_in_method(self):
"""Sets attributes from within a normal method"""
self.att_class = 'set_in_method'
self.att_clsFnSet = 'set_in_method'
if __name__ == '__main__':
print '__without init'
print '> calling `Example.att_class` directly'
fmt_atts(Example, 'att_class')
print '# comment: notice that `A.att_class` has a persisting value'
Example.set_atts_in_classmethod()
Example.view_atts_from_classmethod()
print '\n__ post init: ex = Example()'
ex = Example()
print '# comment: notice that `self.att_initSet` has been set but not accessible from classmethod'
ex.set_atts_in_classmethod()
ex.view_atts_from_classmethod()
print '# comment: notice that `self.att_initSet` was set in __init__ but not avialable!'
print '# comment: however, `self.att_class` was set in another classmethod but *IS* accessible'
ex.view_atts_from_method()
print '# comment: notice that `self.att_initSet` is accessible from a normal method'
ex.set_atts_in_method()
ex.view_atts_from_classmethod()
print '# comment: It appears that classmethods can only access attributes set by other classmethods'
print '# comment: even when instanciated'
输出示例
__without init
> calling `Example.att_class` directly
self.att_class = set_in_class
# comment: notice that `A.att_class` has a persisting value
> calling func: set_atts_in_classmethod :: Sets attributes from within a classmethod
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet does not exist
> end func: set_atts_in_classmethod
> calling func: view_atts_from_classmethod :: View attributes from a classmethod
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet does not exist
> end func: view_atts_from_classmethod
__ post init: ex = Example()
> calling func: __init__ :: setting an attribute and calling self.test2
> calling func: set_atts_in_classmethod :: Sets attributes from within a classmethod
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet does not exist
> end func: set_atts_in_classmethod
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet = set_in_init
> end func: __init__
# comment: notice that `self.att_initSet` has been set but not accessible from classmethod
> calling func: set_atts_in_classmethod :: Sets attributes from within a classmethod
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet does not exist
> end func: set_atts_in_classmethod
> calling func: view_atts_from_classmethod :: View attributes from a classmethod
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet does not exist
> end func: view_atts_from_classmethod
# comment: notice that `self.att_initSet` was set in __init__ but not avialable!
# comment: however, `self.att_class` was set in another classmethod but *IS* accessible
> calling func: view_atts_from_method :: View attributes from a normal method
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet = set_in_init
> end func: view_atts_from_method
# comment: notice that `self.att_initSet` is accessible from a normal method
> calling func: set_atts_in_method :: Sets attributes from within a normal method
self.att_class = set_in_method
self.att_clsFnSet = set_in_method
self.att_initSet = set_in_init
> end func: set_atts_in_method
> calling func: view_atts_from_classmethod :: View attributes from a classmethod
self.att_class = set_in_classmethod
self.att_clsFnSet = set_in_classmethod
self.att_initSet does not exist
> end func: view_atts_from_classmethod
# comment: It appears that classmethods can only access attributes set by other classmethods
# comment: even when instantiated
为什么 class 方法在实例化时对普通方法视而不见?
Class(或静态)方法并不意味着作用于实例。它们是 class 的所有实例 共有的方法。因此,class 方法作用于一个特定实例是不合逻辑的。
这些通常是实用功能。例如,Vector
class 可以将 dot_product
实现为 class 方法,而不是实例方法。这是有道理的,因为这样的操作涉及多个实例。
有没有办法传递 self 以允许 class方法在实例化时看到正常方法?
是的,有,虽然它没有真正意义。
首先,class 方法 能够 查看实例方法。例如:
class Foo:
def speak(self):
print("Hello")
@classmethod
def make_speak(cls, instance):
instance.speak()
foo = Foo()
Foo.make_speak(foo)
输出:
Hello
让我们更进一步,稍微改变一下 make_speak
方法:
@classmethod
def make_speak(cls):
print(cls.speak)
Foo.make_speak()
输出:
<function Foo.speak at 0x000001E58331EF28>
此输出显示,cls.speak
引用的 speak
方法实际上对应于尚未附加到任何实例的 speak
方法。调用该函数时可以看得更清楚:
@classmethod
def make_speak(cls):
cls.speak()
Foo.make_speak()
输出:
TypeError: speak() missing 1 required positional argument: 'self'
引发TypeError
,因为Foo
class 中的speak
方法需要一个未传递的参数。
解决方法
免责声明:以下不是一个好的解决方案。
speak
方法需要一个实例作为参数才能成为 运行。那么,就给它吧:
@classmethod
def make_speak(cls, instance):
cls.speak(instance)
foo = Foo()
Foo.make_speak(foo)
输出:
Hello
但是通过这样做,您希望make_speak
方法作用于特定的Foo
实例,从而调用它的speak
方法。
这就相当于直接调用foo.speak
.
这个例子可能看起来很明显而且乏味。然而,它强调了一个事实,即如果一个 class 方法只接受一个实例作为参数(可能还有其他不是这个 class 实例的东西),那么它 等价于一个实例方法.