Soru Bir nesne özniteliğine bir işlev atama


Benim anlayışına dayanarak Python'un veri modelive özellikle de "Örnek Yöntemler" alt bölümü, "kullanıcı tanımlı işlev" türünde bir özniteliği okuduğunuzda, bazı sihir, gerçek, özgün işlev yerine bir sınır örnek yöntemini alır ve alırsınız. Bu sihir, neden açıkça self Bir yöntemi çağırırken parametre.

Ama sonra, bir nesnenin yöntemini aynı imzaya sahip bir işlevle değiştirmeyi beklerdim:

class Scriptable:
    def __init__(self, script = None):
        if script is not None:
            self.script = script   # replace the method
    def script(self):
        print("greetings from the default script")

>>> scriptable = Scriptable()
>>> scriptable.script()
greetings from the default script

>>> def my_script(self):
...     print("greetings from my custom script")
...
>>> scriptable = Scriptable(my_script)
>>> scriptable.script()
Traceback (most recent call last):
  ...
TypeError: script() takes exactly 1 positional argument (0 given)

Bir örnek oluşturuyorum Scriptableve ayarlanmış script Sınıfta tanımlanmış olan gibi, tek bir parametre ile kullanıcı tanımlı bir işleve öznitelik. Ben okurken scriptable.script özniteliği, sihirin tekme atmasını beklerdim ve hiçbir parametre almayan (örneğin aldığımda aldığım gibi) bir sınırsız örnek yöntemi ver script). Bunun yerine, geçtiğim aynı işlevi geri veriyor gibi görünüyor. self parametre ve hepsi. Yöntem bağlayıcı sihir gerçekleşmiyor.

Metodu bağlayan sihir, sınıf beyannamesi içinde bir yöntem tanımladığımda, ancak özniteliği atadığım zaman neden çalışmaz? Python bu durumları farklı şekilde tedavi eden nedir?

Herhangi bir fark yaratırsa Python3 kullanıyorum.


21
2018-06-25 14:28


Menşei


Çoğu sihirle olduğu gibi, bu hakkı elde edersem tam olarak bunu yapmak için tasarlandı: "Kullanıcı tanımlı fonksiyonların da dikkate alınması önemlidir." sınıf örneğinin özellikleri Hangi değil bağlı yöntemlere dönüştürülür; Bu sadece işlev bir sınıfın niteliği. "İfadeyle self.script Örnek özniteliği oluşturuyorsunuz. Statik yöntem Scriptable.script hala orada. Olası bir geçici çözüm, enjekte edildiğinizi aramak olabilir script eskide script yöntem. - ba__friend


Cevaplar:


İşte böyle yapıyorsun:

import types
class Scriptable:
    def __init__(self, script = None):
        if script is not None:
            self.script = types.MethodType(script, self)   # replace the method
    def script(self):
        print("greetings from the default script")

Ba__friend yorumlarda belirtildiği gibi, yöntemler class nesne. Sınıf nesnesindeki bir tanımlayıcı, özniteliğe bir örnekten eriştiğinizde bağlı yöntemler olarak işlevler döndürür.

Bir işlevi bir instance hiçbir şey olmuyor özel olarak gerçekleşir, bu yüzden işlevi kendiniz sarmanız gerekir.


13
2018-06-25 15:01



Güzel! Ba__friend'ın yorumunu okuduktan sonra yapabileceğimi buldum self.script = lambda: script(self)İstenen sonuçları elde etmek için, ancak bu parametresiz bir yöntem için kodlanmış. Seninki çok daha iyi. - Joe White
Kontrol edin daha iyi script emin olmak için sadece kontrol edilebilir - kentwait


Alex Martelli'ye teşekkürler Cevap işte başka bir versiyon:

class Scriptable:
    def script(self):
        print(self)
        print("greetings from the default script")

def another_script(self):
    print(self)
    print("greetings from the another script")

s = Scriptable()
s.script()

# monkey patching:
s.script = another_script.__get__(s, Scriptable)
s.script()

9
2018-06-25 19:03



Çılgın. Otomatik olarak işlevlerin olduğunu bilmiyordum __get__ öznitelikler - dokümantasyon onların yaptıklarından bile bahsetmez - ancak örneğini yürütürken, onların yaptığını görebilirim. Yani yap lambdas. Yani her fonksiyon otomatik olarak bir tanımlayıcı olarak kullanılabilir ... Python beni şaşırtmaya devam ediyor. Özel gövdeli olduğunu düşündüğüm şeyler genel olarak ortaya çıktı. - Joe White


Şuna bak:

>>> scriptable = Scriptable()
>>> scriptable.script
<bound method Scriptable.script of <__main__.Scriptable instance at 0x01209DA0>>
>>> scriptable = Scriptable(my_script)
>>> scriptable.script
<function my_script at 0x00CF9730>

Beyan self.script = script onunla bir 'büyü' olmadan, yalnızca bir sınıf nesnenin bir özniteliğini oluşturur.

Beyan def script(self): bir sınıf tanımının içinde bir tanımlayıcı bulunur. self parametre.

Python'daki tanımlayıcılar hakkında daha fazla bilgiyi, belirtilen Veri modeli referansında okuyabilirsiniz: -Tanımlayıcılar uygulanması.

Raymond Hettinger'dan Python'daki tanımlayıcılarla ilgili bir başka harika yazı: Tanımlayıcılar İçin Nasıl Yapılır Kılavuzu.


6
2018-06-25 14:46



Bu da OP'nin tam olarak istediği şeyi yapan oldukça basit bir örnek: irrepupavel.com/documents/python/instancemethod (Google'da "python, sınırlama yöntemi oluştur" için en üst sırada yer aldı). - John Zwinck
Devam eden benzer bir sihir var ama sanmıyorum def script(self): aslında bir tanımlayıcı oluşturur. Eğer yaparsam Scriptable.script = my_scriptsonra her şey beklendiği gibi çalışır ve orada yaratılmış bir tanımlayıcı yoktur. Bu ba__friend'ın yorumu doğru gibi görünüyor: sihir sadece tip, öznitelikleri değil örnek. - Joe White
Tamam, düzeltildim. Warvariuc'un koduyla oynadıktan sonra (stackoverflow.com/questions/6478371/...), fonksiyonları açık Hangi tanımlayıcılar - otomatik olarak __get__ öznitelikleri, herhangi bir yerde belgelenmiş görmüyorum bile. Bunu düşünmüştüm type.__getattr__ işlevler için özel durum kodu vardı, ama aslında onları başka bir tanıtıcı gibi ele alıyor gibi görünüyor. - Joe White


Soruyu gerçekten cevaplayamıyorum niye ya Bu şekilde çalışır, Guido van Rossum'a sormanız gerekecek, ancak size olası bir geçici çözüm önerebilirim:

class Scriptable:
    def __init__(self, script = None):
        self._script = script # replace the method
    def script(self):
        if self._script: return self._script(self)
        return self._defaultscript()
    def _defaultscript(self):
        print("greetings from the default script")

1
2018-06-25 14:47