【问题标题】:Why are classmethods blind to normal methods when instantiated?为什么类方法在实例化时对普通方法视而不见?
【发布时间】:2017-10-16 17:25:11
【问题描述】:

问题

  1. 为什么类方法在实例化时对普通方法视而不见?
  2. 有没有办法通过self 让类方法在实例化时查看普通方法?

观察

未实例化的类方法

  • 使用类方法时,任何属性的修改都会持续存在。
  • 我希望每次使用类函数时只在类方法运行时临时存储属性

实例化的类方法

  • 类方法不能使用任何通过普通方法设置的变量
  • 即使类被实例化,类方法仍然对普通方法中所做的任何事情完全视而不见!
  • 这是意外行为。我希望类方法在实例化后表现得像普通方法!

示例代码

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

【问题讨论】:

  • 从我的手机发帖 - 整理代码语法!
  • 您似乎对类方法的作用以及类属性与实例属性有一些误解。但是在当前状态下阅读您的代码并不容易。
  • 是的,非常抱歉我的工作场所阻塞了堆栈溢出。我不得不将我的问题通过电子邮件发送到我的手机并在此处进行编辑。我想我有办法解决这个格式问题
  • 语法固定:)

标签: python class methods instantiation class-method


【解决方案1】:

为什么类方法在实例化时对普通方法视而不见?

类(或静态)方法并不意味着作用于实例。它们是类的所有实例通用的方法。因此,类方法作用于一个特定的实例是不合逻辑的。

这些通常是实用功能。例如,Vector 类可以将dot_product 实现为类方法,而不是实例方法。这是有道理的,因为这样的操作涉及多个实例。

有没有办法通过 self 让类方法在实例化时看到普通方法?

是的,有,但它并不真正有意义。

首先,类方法能够查看实例方法。例如:

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 类中的speak 方法需要一个未传递的参数。

解决方法

免责声明:以下是不是一个好的解决方案。

speak 方法需要一个实例作为参数才能运行。然后,给它:

@classmethod
def make_speak(cls, instance):
    cls.speak(instance)

foo = Foo()
Foo.make_speak(foo)

输出:

Hello

但是通过这样做,您希望make_speak 方法作用于特定的Foo 实例,从而调用其speak 方法。

这完全等于直接调用foo.speak


这个例子可能看起来很明显和乏味。但是,它强调了这样一个事实,即如果一个类方法只接受一个实例作为参数(以及可能不是该类实例的其他事物),那么它相当于一个实例方法

【讨论】:

  • 我希望我编辑的格式能让它更清晰。为之前的混乱道歉! classmethod 似乎无法正确“看到”实例方法,因为它无法与操作期间设置/修改或设置的实例属性交互。有什么解决方法吗?还是我误解了他们的目的?
  • @AlexanderMcFarlane 我回答完了。是不是更清楚了?
  • 今天下午我会看一下 - 感谢您提供额外的详细信息
猜你喜欢
  • 2019-01-13
  • 2016-06-11
  • 1970-01-01
  • 2016-01-13
  • 1970-01-01
  • 1970-01-01
  • 2016-04-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多