classmethod和instancemethod的名称相同吗?

Mar*_*nen 10 python methods class-method python-3.x

我想做这样的事情:

class X:

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

    def id(self):
        return self.__class__.__name__
Run Code Online (Sandbox Code Playgroud)

现在要求id()它的类或它的实例:

>>> X.id()
'X'
>>> X().id()
'X'
Run Code Online (Sandbox Code Playgroud)

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

Ash*_*ary 19

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

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()
Run Code Online (Sandbox Code Playgroud)

输出:

Called by an instance.
X
Called by a class.
X
Run Code Online (Sandbox Code Playgroud)


Mar*_*ers 13

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

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

class X:
    @classmethod
    def id(cls):
        return cls.__name__
Run Code Online (Sandbox Code Playgroud)

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

>>> class X:
...     @classmethod
...     def id(cls):
...         return cls.__name__
... 
>>> X.id()
'X'
>>> X().id()
'X'
Run Code Online (Sandbox Code Playgroud)

这是明确记录的:

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

  • 问题是构造函数或其他实例数据中的任何参数都无法从类方法访问,因此您的“解决方案”非常有限。 (2认同)

M. *_*ght 8

通过将方法的实例绑定版本显式绑定实例(而不是类),可以非常简洁地完成它.Python将调用在调用时找到的实例属性(因为它在类之前搜索实例),并且在调用when时找到类绑定方法.Class().__dict__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')
Run Code Online (Sandbox Code Playgroud)

>>> Test.check()
Called as class
>>> Test().check()
Called as instance, probably
Run Code Online (Sandbox Code Playgroud)

或者......让我们说我们希望能够滥用以下内容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)
Run Code Online (Sandbox Code Playgroud)

>>> 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']]
Run Code Online (Sandbox Code Playgroud)

  • @ipetrik 我主要是这么认为的,因为它更容易规避,也更草率:它使用 hack-y 技术来确定它是否是一个实例,而描述符则由 Python 本身传递该信息。它还要求您在`__init__` 中为*每个方法* 执行`self.x = self.__x` 赋值(虽然这可以卸载到+ 由元类或装饰器自动化,我认为)......最后猴子补丁通常不太好。但这绝对只是一个意见——如果你发现这个更清洁,那就去吧! (2认同)

小智 5

自 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__)
Run Code Online (Sandbox Code Playgroud)

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

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

这可能有点矫枉过正,尤其是如果您想远离元类。这是我想在我身边探索的一个技巧,所以我只想分享这颗隐藏的宝石,以防你可以擦亮它并让它发光!

>>> X().id
'Instance X'
>>> X.id
'Class X'
Run Code Online (Sandbox Code Playgroud)

瞧...