Metodu tanımlayan sınıfı alın


89

Python'da bir metodu tanımlayan sınıfı nasıl edinebilirim?

Aşağıdaki örneğin " __main__.FooClass" yazdırmasını istiyorum :

class FooClass:
    def foo_method(self):
        print "foo"

class BarClass(FooClass):
    pass

bar = BarClass()
print get_class_that_defined_method(bar.foo_method)

Python'un hangi sürümünü kullanıyorsunuz? 2.2'den önce im_class'ı kullanabiliyordunuz, ancak bu bağlı öz nesnenin türünü göstermek için değiştirildi.
Kathy Van Stone

1
Bunu bildiğim iyi oldu. Ama 2.6 kullanıyorum.
Jesse Aldridge

Yanıtlar:


70
import inspect

def get_class_that_defined_method(meth):
    for cls in inspect.getmro(meth.im_class):
        if meth.__name__ in cls.__dict__: 
            return cls
    return None

1
Teşekkürler, bunu kendi başıma çözmem biraz zaman alırdı
David

Dikkat edin, tüm sınıflar uygulanmaz __dict__! Bazen __slots__kullanılır. getattrYöntemin sınıfta olup olmadığını test etmek için kullanmak muhtemelen daha iyidir .
Codie CodeMonkey

16
Bunun için Python 3lütfen bu cevaba bakınız .
Yoel

27
'function' object has no attribute 'im_class'
Alıyorum

5
Python 2.7'de çalışmıyor. 'İm_class' eksikliğiyle aynı hata.
RedX

8

Noktayı kaçırdığımı belirttiğim için teşekkürler Sr2222 ...

İşte Alex'inki gibi olan ancak hiçbir şey içe aktarmayı gerektirmeyen düzeltilmiş yaklaşım. Bununla birlikte, devralınan sınıfların devasa bir hiyerarşisi olmadığı sürece, bu yaklaşım tanımlayıcı sınıf bulunur bulunmaz, tüm mirası olduğu gibi geri döndürmek yerine durduğu için bunun bir gelişme olduğunu düşünmüyorum getmro. Dediğim gibi, bu bir çok olası senaryo.

def get_class_that_defined_method(method):
    method_name = method.__name__
    if method.__self__:    
        classes = [method.__self__.__class__]
    else:
        #unbound method
        classes = [method.im_class]
    while classes:
        c = classes.pop()
        if method_name in c.__dict__:
            return c
        else:
            classes = list(c.__bases__) + classes
    return None

Ve Örnek:

>>> class A(object):
...     def test(self): pass
>>> class B(A): pass
>>> class C(B): pass
>>> class D(A):
...     def test(self): print 1
>>> class E(D,C): pass

>>> get_class_that_defined_method(A().test)
<class '__main__.A'>
>>> get_class_that_defined_method(A.test)
<class '__main__.A'>
>>> get_class_that_defined_method(B.test)
<class '__main__.A'>
>>> get_class_that_defined_method(C.test)
<class '__main__.A'>
>>> get_class_that_defined_method(D.test)
<class '__main__.D'>
>>> get_class_that_defined_method(E().test)
<class '__main__.D'>
>>> get_class_that_defined_method(E.test)
<class '__main__.D'>
>>> E().test()
1

Alex çözümü aynı sonuçları verir. Alex yaklaşımı kullanılabildiği sürece, bunun yerine onu kullanırdım.


Cls().meth.__self__sadece size Clsbunun belirli bir örneğine bağlı olan örneğini verir meth. Benzer Cls().meth.im_class. Eğer sahipseniz class SCls(Cls), SCls().meth.__self__size bir SClsörnek değil, bir örnek alırsınız Cls. OP'nin istediği şey elde etmektir Cls, görünen o ki, @Alex Martelli'nin yaptığı gibi yalnızca MRO'da yürüyerek elde edilebilir.
Silas Ray

@ sr2222 Haklısın. Alex çözümünün daha kompakt olduğunu düşünmeme rağmen, daha önce başladığım gibi yanıtı değiştirdim.
estani

1
İthalattan kaçınmanız gerekiyorsa bu iyi bir çözümdür, ancak temelde sadece MRO'yu yeniden uyguladığınız için sonsuza kadar çalışacağı garanti edilmez. MRO muhtemelen aynı kalacak, ancak Python'un geçmişinde bir kez zaten değiştirilmişti ve eğer tekrar değiştirilirse, bu kod ince, yaygın hatalarla sonuçlanacaktır.
Silas Ray

Programlamada defalarca "pek olası olmayan senaryolar" oluyor. Nadiren felaketlere neden olur. Genel olarak, "XY.Z% asla olmayacak" düşünce kalıbı , kodlama yaparken son derece kötü bir düşünme aracıdır. Onu kullanma. % 100 doğru kodu yazın.
ulidtko

@ulidtko Bence açıklamayı yanlış anladınız. Doğrulukla değil, hızla ilgili. Tüm durumlara uyan "mükemmel" bir çözüm yoktur, aksi takdirde örneğin yalnızca bir sıralama algoritması olacaktır. Burada önerilen çözüm, "nadir" durumda daha hızlı olmalıdır. Hız, okunabilirlikten sonra tüm vakaların% 99'unda geldiğinden, bu çözüm "yalnızca" bu nadir durumlarda daha iyi bir çözüm olabilir. Korktuğunuz şey buysa, kod, okumamış olmanız durumunda% 100 doğrudur.
estani

6

Neden hiç kimsenin bunu gündeme getirmediğini veya neden en iyi cevabın cehennem kadar yavaşken 50 ek oyu olduğunu bilmiyorum, ancak aşağıdakileri de yapabilirsiniz:

def get_class_that_defined_method(meth):
    return meth.im_class.__name__

Python 3 için bunun değiştiğine inanıyorum ve araştırmanız gerekecek .__qualname__.


2
Hmm. Bunu python 2.7'de yaptığım zaman tanımlayıcı sınıfı görmüyorum - yöntemin çağrıldığı sınıfı alıyorum, tanımlandığı yeri değil ...
F1Rumors

5

Python 3'te, gerçek sınıf nesnesine ihtiyacınız varsa şunları yapabilirsiniz:

import sys
f = Foo.my_function
vars(sys.modules[f.__module__])[f.__qualname__.split('.')[0]]  # Gets Foo object

İşlev iç içe geçmiş bir sınıfa aitse, aşağıdaki gibi yineleme yapmanız gerekir:

f = Foo.Bar.my_function
vals = vars(sys.modules[f.__module__])
for attr in f.__qualname__.split('.')[:-1]:
    vals = vals[attr]
# vals is now the class Foo.Bar

2

Python 3

Çok basit bir şekilde çözdüm:

str(bar.foo_method).split(" ", 3)[-2]

Bu verir

'FooClass.foo_method'

Sınıfı ve işlev adını ayrı ayrı almak için noktayı ayırın


Vay be ne kurtarış!
user3712978

1

Biraz benzer bir şey yapmaya başladım, temelde fikir, temel sınıftaki bir yöntemin bir alt sınıfta uygulanıp uygulanmadığını kontrol etmekti. Başlangıçta yaptığım gibi ortaya çıktı, bir ara sınıfın yöntemi gerçekten uyguladığını tespit edemedim.

Benim çözümüm aslında oldukça basitti; bir yöntem özelliği belirleme ve daha sonra varlığını test etme. İşte her şeyin basitleştirilmesi:

class A():
    def method(self):
        pass
    method._orig = None # This attribute will be gone once the method is implemented

    def run_method(self, *args, **kwargs):
        if hasattr(self.method, '_orig'):
            raise Exception('method not implemented')
        self.method(*args, **kwargs)

class B(A):
    pass

class C(B):
    def method(self):
        pass

class D(C):
    pass

B().run_method() # ==> Raises Exception: method not implemented
C().run_method() # OK
D().run_method() # OK

GÜNCELLEME: Aslında call method()dan run_method()(? Ruhu bu değil) ve yönteme değiştirilmemiş bütün argümanları geçmesi gerekir.

Not: Bu yanıt, soruyu doğrudan yanıtlamaz. IMHO'nun hangi sınıfın bir yöntemi tanımladığını bilmek istemesinin iki nedeni vardır; birincisi, hata ayıklama kodunda (istisna işleme gibi) bir sınıfa parmakları işaret etmektir ve ikincisi, yöntemin yeniden uygulanıp uygulanmadığını belirlemektir (burada yöntem, programcı tarafından uygulanması amaçlanan bir saplamadır). Bu cevap, ikinci durumu farklı bir şekilde çözer.

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.