Sınıf gövdesi içinde sınıf staticmethod çağırıyor musunuz?


159

Sınıfın içinden statik bir yöntem kullanmaya çalıştığımda ve yerleşik staticmethodişlevi bir dekoratör olarak kullanarak statik yöntemi tanımladığımda, şöyle:

class Klass(object):

    @staticmethod  # use as decorator
    def _stat_func():
        return 42

    _ANS = _stat_func()  # call the staticmethod

    def method(self):
        ret = Klass._stat_func() + Klass._ANS
        return ret

Aşağıdaki hatayı alıyorum:

Traceback (most recent call last):<br>
  File "call_staticmethod.py", line 1, in <module>
    class Klass(object): 
  File "call_staticmethod.py", line 7, in Klass
    _ANS = _stat_func() 
  TypeError: 'staticmethod' object is not callable

Bunun neden olduğunu (tanımlayıcı bağlama) anlıyorum_stat_func() ve son kullanımından sonra manuel olarak bir statik metoda dönüştürerek bunun etrafında çalışabilir , şöyle:

class Klass(object):

    def _stat_func():
        return 42

    _ANS = _stat_func()  # use the non-staticmethod version

    _stat_func = staticmethod(_stat_func)  # convert function to a static method

    def method(self):
        ret = Klass._stat_func() + Klass._ANS
        return ret

Benim sorum şu:

Daha temiz ya da daha fazla "Pitonik" gibi bunu başarmak için daha iyi yollar var mı?


4
Pythonicity hakkında soru soruyorsanız, standart tavsiye hiç kullanmamaktır staticmethod. Genellikle modül düzeyinde işlevler olarak daha kullanışlıdır, bu durumda sorununuz bir sorun değildir. classmethod, Öte yandan ...
Benjamin Hodgson

1
@poorsod: Evet, bu alternatifin farkındayım. Ancak bu sorunla karşılaştığım gerçek kodda, işlevi modül düzeyinde koymak yerine statik bir yöntem haline getirmek, sorumda kullanılan basit örnekte olduğundan daha mantıklı.
martineau

Yanıtlar:


180

staticmethodnesneler __func__orijinal ham fonksiyonu saklayan bir özelliğe sahiptir (zorunda oldukları mantıklıdır). Yani bu işe yarayacak:

class Klass(object):

    @staticmethod  # use as decorator
    def stat_func():
        return 42

    _ANS = stat_func.__func__()  # call the staticmethod

    def method(self):
        ret = Klass.stat_func()
        return ret

Bir kenara, bir staticmethod nesnesinin orijinal işlevi saklayan bir tür niteliğe sahip olduğundan şüphelenmeme rağmen, özellikler hakkında hiçbir fikrim yoktu. Birisine balık vermek yerine balık tutmayı öğretmek ruhu içinde, bunu araştırmak ve bulmak için yaptım (Python oturumumdan bir C&P):

>>> class Foo(object):
...     @staticmethod
...     def foo():
...         return 3
...     global z
...     z = foo

>>> z
<staticmethod object at 0x0000000002E40558>
>>> Foo.foo
<function foo at 0x0000000002E3CBA8>
>>> dir(z)
['__class__', '__delattr__', '__doc__', '__format__', '__func__', '__get__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> z.__func__
<function foo at 0x0000000002E3CBA8>

Etkileşimli bir oturumda benzer kazma türleri ( dirçok yararlıdır) genellikle bu tür soruları çok hızlı bir şekilde çözebilir.


İyi güncelleme ... Belgede görmediğim için bunu nasıl bildiğinizi sormak üzereydim - ki bu bir "uygulama detayı" olabileceği için onu kullanma konusunda biraz gergindir.
martineau

Daha fazla okumadan sonra __func__bunun sadece başka bir isim olduğunu im_funcve Python 3 ileriye doğru uyumluluk için Py 2.6'ya eklendiğini görüyorum .
martineau

2
Görüyorum ki teknik olarak bu bağlamda belgesiz.
martineau

1
@AkshayHazari __func__Statik bir yöntemin özniteliği, sanki staticmethoddekoratörü hiç kullanmamışsınız gibi orijinal işleve bir başvuru sağlar . Bu nedenle işleviniz bağımsız değişkenler gerektiriyorsa, çağrı yaparken bunları iletmeniz gerekir __func__. Hata mesajı ona hiç argüman vermemiş gibi geliyor. Eğer stat_funcbu yayının örnekte iki argüman aldı şunları kullanırsınız_ANS = stat_func.__func__(arg1, arg2)
Ben

1
@AkshayHazari Anladığımdan emin değilim. Değişkenler olabilirler, sadece kapsam içi değişkenler olmalıdırlar: ya daha önce sınıfın tanımlandığı yerde aynı kapsamda tanımlanırlar (genellikle global) ya da daha önce sınıf kapsamı içinde tanımlanırlar ( stat_funckendisi böyle bir değişkendir). Tanımladığınız sınıfın örnek niteliklerini kullanamayacağınızı mı kastettiniz? Bu doğru, ama şaşırtıcı değil; biz yok var biz hala sınıfı tanımlarken beri sınıfının bir örneğini! Ama her neyse, onları sadece geçmek istediğiniz argümanlar için standin demek istedim; orada gerçekleri kullanabilirsiniz.
Ben

24

Bu benim tercih ettiğim yol:

class Klass(object):

    @staticmethod
    def stat_func():
        return 42

    _ANS = stat_func.__func__()

    def method(self):
        return self.__class__.stat_func() + self.__class__._ANS

Bu çözümü KURU prensibiKlass.stat_func nedeniyle tercih ediyorum . Bana Python 3'te yeni bir neden olduğunu hatırlatıyor :)super()

Ama diğerlerine katılıyorum, genellikle en iyi seçim bir modül seviyesi fonksiyonu tanımlamaktır.

İle Örneğin @staticmethodfonksiyonu, tekrarlama (Sen arayarak KURU prensibini kırmak gerekir çok iyi görünmeyebilir Klass.stat_funciçini Klass.stat_func). Çünkü selfiç statik yönteme başvurunuz yok . Modül seviyesi fonksiyonu ile her şey iyi görünecektir.


self.__class__.stat_func()Düzenli yöntemlerde kullanmanın kullanmanın avantajlarına (DRY ve her şeye) sahip olduğunu kabul etsem de Klass.stat_func(), bu gerçekten benim sorumun konusu değildi - aslında ilkini tutarsızlık konusunu bulutlandırmak için kullanmaktan kaçındım.
martineau

1
Bu gerçekten de DRY meselesi değil. self.__class__daha iyidir çünkü bir alt sınıf geçersiz kılınırsa stat_func, Subclass.methodalt sınıfı çağırır stat_func. Ancak tamamen dürüst olmak gerekirse, bu durumda statik bir yöntem yerine gerçek bir yöntem kullanmak çok daha iyidir.
asmeurer

@asmeurer: Gerçek bir yöntem kullanamıyorum çünkü henüz sınıf örneği oluşturulmadı - sınıfın kendisi henüz tam olarak tanımlanmadığı için olamazlar.
martineau

Sınıfım için statik yöntemi çağırmak için bir yol istedim (devralınan; böylece üst aslında işlevi vardır) ve bu bugüne kadar en iyi görünüyor (ve daha sonra geçersiz kılarsa yine de çalışır).
whitey04

11

Sınıf tanımından sonra class niteliğini enjekte etmeye ne dersiniz?

class Klass(object):

    @staticmethod  # use as decorator
    def stat_func():
        return 42

    def method(self):
        ret = Klass.stat_func()
        return ret

Klass._ANS = Klass.stat_func()  # inject the class attribute with static method value

1
Bu benim etraftaki bir çalışmadaki ilk girişimlerime benzer, ama sınıfın içinde olan bir şeyi tercih ederdim ... kısmen söz konusu özelliğin önde gelen alt çizgisi olduğu ve özel olduğu için.
martineau

11

Bunun nedeni, statik yöntemin bir tanımlayıcı olması ve tanımlayıcı protokolünü kullanmak ve gerçek çağrılabilir olanı almak için sınıf düzeyinde bir öznitelik gerektirir.

Kaynak koddan:

Sınıf (örneğin C.f()) veya bir örnek (örneğin C().f()) üzerinde çağrılabilir ; sınıf, sınıfı dışında yok sayılır.

Ancak tanımlanırken doğrudan sınıfın içinden değil.

Ancak bir yorumcunun da bahsettiği gibi, bu gerçekten bir "Pitonik" tasarım değil. Bunun yerine bir modül seviyesi işlevi kullanın.


Sorumda söylediğim gibi, orijinal kodun neden çalışmadığını anlıyorum. Neden pythonic olarak kabul edildiğini açıklayabilir (veya bunu yapan bir şeye bağlantı sağlayabilir misiniz?)
martineau

Statik yöntem, sınıf nesnesinin kendisinin düzgün çalışmasını gerektirir. Ancak sınıf düzeyinde sınıf içinden çağırırsanız, sınıf bu noktada tam olarak tanımlanmamıştır. Yani henüz referans veremezsiniz. Statik metodu tanımlandıktan sonra sınıfın dışından çağırmak uygundur. Ancak " Pythonic " aslında estetik gibi iyi tanımlanmamıştır. Size bu kodla ilgili ilk izlenimin olumlu olmadığını söyleyebilirim.
Keith

Hangi sürümün (veya her ikisinin) gösterimi? Ve neden, özellikle?
martineau

Sadece nihai hedefinizin ne olduğunu tahmin edebilirim, ama bana öyle geliyor ki dinamik, sınıf düzeyinde bir özellik istiyorsunuz. Bu, metasınıflar için bir iş gibi görünüyor. Ancak yine de, işlevselliği feda etmeden ortadan kaldıran ve basitleştiren tasarıma bakmanın daha basit bir yolu veya başka bir yolu olabilir.
Keith

3
Hayır, ne yapmak istiyorum aslında oldukça basit - bazı ortak kodu dışarı ve yeniden kullanmak, hem sınıf oluşturma zamanında özel bir sınıf özniteliği oluşturmak için ve daha sonra bir veya daha fazla sınıf yöntemleri içinde. Bu ortak kod sınıf dışında hiçbir kullanımı vardır, bu yüzden doğal olarak bunun bir parçası olmasını istiyorum. Metasınıf (veya sınıf dekoratörü) kullanmak işe yarayacaktır, ancak bu kolay olması gereken bir şey için aşırıya kaçmış gibi görünüyor, IMHO.
martineau

8

Bu çözüm ne olacak? @staticmethodDekoratör uygulaması bilgisine dayanmaz . İç sınıf StaticMethod statik başlatma işlevlerinin bir taşıyıcısı olarak oynatılır.

class Klass(object):

    class StaticMethod:
        @staticmethod  # use as decorator
        def _stat_func():
            return 42

    _ANS = StaticMethod._stat_func()  # call the staticmethod

    def method(self):
        ret = self.StaticMethod._stat_func() + Klass._ANS
        return ret

2
Yaratıcılık için +1, ancak __func__artık resmi olarak belgelendiği için kullanmaktan endişe etmiyorum (bkz . Python 2.7'deki Yeniliklerin Diğer Dil Değişiklikleri ve Sayı 5982'ye referans ). Çözümünüz daha da portatiftir, çünkü muhtemelen 2.6'dan önceki Python sürümlerinde de ( __func__eşanlamlı olarak tanıtıldığında im_func) çalışacaktır .
martineau

Bu Python 2.6 ile çalışan tek çözümdür.
benselme

@benselme: Python 2.6 yüklü olmadığım için iddianızı doğrulayamıyorum, ancak ciddi şüphe sadece tek ...
martineau
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.