classmethod和instancemethod的名称相同吗?

问题描述 投票:9回答:6

我想做这样的事情:

class X:

    @classmethod
    def id(cls):
        return cls.__name__

    def id(self):
        return self.__class__.__name__

现在将id()称为类或其实例:

>>> X.id()
'X'
>>> X().id()
'X'

显然这个确切的代码不起作用,但有没有类似的方法使它工作?或者任何其他解决方法来获得这样的行为没有太多“hacky”的东西?

python python-3.x methods class-method
6个回答
11
投票

类和实例方法存在于同一名称空间中,您不能重用这样的名称;在这种情况下,id的最后定义将获胜。

类方法将继续在实例上工作,但是,不需要创建单独的实例方法;只需使用:

class X:
    @classmethod
    def id(cls):
        return cls.__name__

因为该方法继续绑定到类:

>>> class X:
...     @classmethod
...     def id(cls):
...         return cls.__name__
... 
>>> X.id()
'X'
>>> X().id()
'X'

这是明确记录的:

它可以在类(例如C.f())或实例(例如C().f())上调用。除了类之外,该实例被忽略。


20
投票

不知道你的实际用例是什么,但你可以使用描述符做这样的事情:

class Desc(object):

    def __get__(self, ins, typ):
        if ins is None:
            print 'Called by a class.'
            return lambda : typ.__name__
        else:
            print 'Called by an instance.'
            return lambda : ins.__class__.__name__

class X(object):
    id = Desc()

x = X()
print x.id()
print X.id()

输出:

Called by an instance.
X
Called by a class.
X

7
投票

通过将方法的实例绑定版本显式绑定到实例(而不是类),可以非常简洁地完成它。当调用Class().__dict__时,Python将调用Class().foo()中找到的实例属性(因为它在类'之前搜索实例的__dict__),并且在调用Class.__dict__时在Class.foo()中找到类绑定方法。

这有许多潜在的用例,但是它们是否反模式是有争议的:

class Test:
    def __init__(self):
        self.check = self.__check

    @staticmethod
    def check():
        print('Called as class')

    def __check(self):
        print('Called as instance, probably')

>>> Test.check()
Called as class
>>> Test().check()
Called as instance, probably

或者......让我们说我们希望能够滥用像map()这样的东西:

class Str(str):
    def __init__(self, *args):
        self.split = self.__split

    @staticmethod
    def split(sep=None, maxsplit=-1):
        return lambda string: string.split(sep, maxsplit)

    def __split(self, sep=None, maxsplit=-1):
        return super().split(sep, maxsplit)

>>> s = Str('w-o-w')
>>> s.split('-')
['w', 'o', 'w']
>>> Str.split('-')(s)
['w', 'o', 'w']
>>> list(map(Str.split('-'), [s]*3))
[['w', 'o', 'w'], ['w', 'o', 'w'], ['w', 'o', 'w']]

3
投票

“类型”提供了一些非常有趣的东西,因为Python 3.4:DynamicClassAttribute

它不是你想要的100%,但它似乎是密切相关的,你可能需要稍微调整一下我的元类但是,粗鲁的,你可以拥有它;

from types import DynamicClassAttribute

class XMeta(type):
     def __getattr__(self, value):
         if value == 'id':
             return XMeta.id  # You may want to change a bit that line.
     @property
     def id(self):
         return "Class {}".format(self.__name__)

这将定义您的类属性。对于实例属性:

class X(metaclass=XMeta):
    @DynamicClassAttribute
    def id(self):
        return "Instance {}".format(self.__class__.__name__)

它可能有点矫枉过正,特别是如果你想远离元类。这是我想在我身边探索的一个技巧,所以我只是想分享这个隐藏的宝石,万一你可以抛光它并让它闪耀!

>>> X().id
'Instance X'
>>> X.id
'Class X'

瞧...


1
投票

在您的示例中,您可以完全删除第二个方法,因为static方法和类方法都做同样的事情。

如果你想让他们做不同的事情:

class X:
    def id(self=None):
       if self is None:
           # It's being called as a static method
       else:
           # It's being called as an instance method

1
投票

(仅限Python 3)在阐述a pure-Python implementation of @classmethod的想法时,我们可以将@class_or_instance_method声明为装饰器,它实际上是一个实现attribute descriptor protocol的类:

import inspect


class class_or_instance_method(object):

    def __init__(self, f):
        self.f = f

    def __get__(self, instance, owner):
        if instance is not None:
            wrt = instance
        else:
            wrt = owner

        def newfunc(*args, **kwargs):
            return self.f(wrt, *args, **kwargs)
        return newfunc

class A:
    @class_or_instance_method
    def foo(self_or_cls, a, b, c=None):
        if inspect.isclass(self_or_cls):
            print("Called as a class method")
        else:
            print("Called as an instance method")
© www.soinside.com 2019 - 2024. All rights reserved.