classmethod和instancemethod的名称相同吗?

时间:2015-01-30 14:25:29

标签: python python-3.x methods class-method

我想做这样的事情:

class X:

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

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

现在为类或其实例调用id()

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

显然这段确切的代码并不起作用,但有没有类似的方法让它起作用? 或任何其他解决方法,以获得这种行为没有太多" hacky"东西?

6 个答案:

答案 0 :(得分: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

答案 1 :(得分: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())上调用它。除了类之外,该实例被忽略。

答案 2 :(得分:5)

通过将方法的实例绑定版本显式绑定到实例(而不是类),可以非常简洁地完成 。调用Class().__dict__时,Python将调用Class().foo()中的实例属性(因为它在类&#39;之前搜索实例&#39; s __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 :(得分:2)

“类型”自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属性。对于instance属性:

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

这可能有点矫kill过正,尤其是如果您想远离元类。我想在自己这边探索这个技巧,所以我只想分享这个隐藏的宝石,以防您可以抛光它并使它发光!

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

Voila ...

答案 4 :(得分:1)

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

如果您希望他们做其他事情:

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

答案 5 :(得分:0)

(仅适用于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")