簡體   English   中英

制作既是類方法又是實例方法的方法

[英]Make a method that is both a class method and an instance method

Python中有沒有辦法定義既是類方法又是實例方法的方法,使得clsself都是接收者。 特別是,我正在尋找一種方法,該方法(1)知道在類( Foo.method(value) )上調用時調用了哪個類,以及(2)知道在實例上調用時調用了哪個實例。 ( foo.method() )。

例如,我可以設想這樣的工作:

class Foo:
    def __str__(self): return 'Foo()'

    @classinstancemethod
    def method(cls, self):
        print(cls, self)

class Bar(Foo):
    def __str__(self): return 'Bar()'

Foo().method()     # <class '__main__.Foo'> Foo()
Bar().method()     # <class '__main__.Bar'> Bar()
Foo.method(Foo())  # <class '__main__.Foo'> Foo()
Foo.method(Bar())  # <class '__main__.Foo'> Bar()
Bar.method(Foo())  # <class '__main__.Bar'> Foo()

請注意,我知道可以像Foo.foo(value)這樣調用未修飾的方法,但這不是我想要的,因為它沒有獲取cls變量。 而且,如果沒有cls變量,則該方法不知道它剛剛調用了哪個類。 它可能被稱為Bar.method(value) ,現在將有辦法知道value是否是Foo的實例。 未修飾的方法更像靜態方法和實例方法,而不是類方法和實例方法。

您不需要為此的裝飾器。 這就是方法已經起作用的方式。 實例作為第一個參數傳遞-隱式地從實例調用方法,並從類顯式調用-在實例可用的情況下,您可以通過在實例上調用type來檢索類:

class Foo(object):
    def __repr__(self): return 'Foo()'

    def method(self):
        print((type(self), self)) 

class Bar(Foo):
    def __repr__(self): return 'Bar()'

另外,在__str__ (或__repr__ )特殊方法中,您應該返回一個字符串,而不是print。

我使用__repr__因為在容器對象(此處為元組)中不調用__str__來打印實例。

更新

考慮到上述方法中類/實例打印的問題,您可以改用描述符來正確管理方法中正確的類和實例選擇:

class classinstancemethod():

  def __get__(self, obj, cls):
     def method(inst=None):
        print(cls, inst if inst else obj)
     return method

class Foo(object):
    method = classinstancemethod()

    def __str__(self): return 'Foo()'


class Bar(Foo):
    def __str__(self): return 'Bar()'

這可以通過將classinstancemethod實現為自定義描述符來解決。

簡而言之,描述符必須定義一個__get__方法,當它作為屬性訪問時將被調用(例如Foo.methodFoo().method )。 該方法將實例和類作為參數傳遞給它,並返回一個綁定方法(即,它返回一個帶有clsself參數的方法)。 調用此綁定方法時,它將已烘焙的clsself參數轉發到實際方法。

class classinstancemethod:
    def __init__(self, method, instance=None, owner=None):
        self.method = method
        self.instance = instance
        self.owner = owner

    def __get__(self, instance, owner=None):
        return type(self)(self.method, instance, owner)

    def __call__(self, *args, **kwargs):
        instance = self.instance
        if instance is None:
            if not args:
                raise TypeError('missing required parameter "self"')
            instance, args = args[0], args[1:]

        cls = self.owner
        return self.method(cls, instance, *args, **kwargs)

結果:

class Foo:
    def __repr__(self): return 'Foo()'

    @classinstancemethod
    def method(cls, self):
        print((cls, self))


class Bar(Foo):
    def __repr__(self): return 'Bar()'


Foo().method()     # (<class '__main__.Foo'>, 'Foo()')
Bar().method()     # (<class '__main__.Bar'>, 'Bar()')
Foo.method(Foo())  # (<class '__main__.Foo'>, 'Foo()')
Foo.method(Bar())  # (<class '__main__.Foo'>, 'Bar()')
Bar.method(Foo())  # (<class '__main__.Bar'>, 'Foo()')

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM