Bir sınıfın tanımlanmış bir işlevi olup olmadığını kontrol etmenin en hızlı yolu nedir?


132

Bir AI durum uzayı arama algoritması yazıyorum ve bir arama algoritmasını hızlı bir şekilde uygulamak için kullanılabilecek genel bir sınıfım var. Bir alt sınıf, gerekli işlemleri tanımlar ve algoritma gerisini halleder.

Burada sıkışıp kaldığım yer: Ebeveyn durumunu tekrar tekrar oluşturmaktan kaçınmak istiyorum, bu nedenle herhangi bir duruma yasal olarak uygulanabilecek işlemleri döndüren aşağıdaki işleve sahibim:

def get_operations(self, include_parent=True):
    ops = self._get_operations()
    if not include_parent and self.path.parent_op:
        try:
            parent_inverse = self.invert_op(self.path.parent_op)
            ops.remove(parent_inverse)
        except NotImplementedError:
            pass
    return ops

Ve invert_op işlevi varsayılan olarak atar.

Fonksiyonun tanımlanıp tanımlanmadığını kontrol etmenin bir istisna yakalamaktan daha hızlı bir yolu var mı?

Dir de şimdiki zamanı kontrol etme satırlarında bir şeyler düşünüyordum, ama bu doğru görünmüyor. hasattr getattr aranarak ve yükselip yükselmediğine bakılarak gerçekleştirilir ki bu benim istediğim şey değil.


8
"Hasattr, getattr aranıp yükselip yükselmediğine bakılarak gerçekleştirilir ki bu benim istediğim değil. Neden olmasın? Uygulamanın ne yaptığını neden umursuyorsun?
detly

4
has_op = lambda obj, op: callable(getattr(obj, op, None))
samplebias

1
Deneyin: hasattr(connection, 'invert_opt').
kenorb

Yanıtlar:


205

Evet, getattr()özelliği almak ve callable()bir yöntem olduğunu doğrulamak için kullanın:

invert_op = getattr(self, "invert_op", None)
if callable(invert_op):
    invert_op(self.path.parent_op)

getattr()Öznitelik olmadığında normalde istisna attığına dikkat edin. Ancak, varsayılan bir değer belirtirseniz ( Nonebu durumda), bunun yerine onu döndürür.


3
Ayrıca, getattrbu durumda uygulamasının sessizce bir istisnayı yakaladığını ve bunun yerine, hasattrOP'nin nedense karşı olduğu gibi, varsayılan değeri döndürdüğünü unutmayın .
Santa

3
Ya işlev o sınıfta değil de üst sınıfta ise ?. Bu durumda, çocuklar asla bu işlevi uygulamasalar bile (hasattr kullanarak)
darkgaze

46

Hem Python 2 hem de Python 3'te çalışır

hasattr(connection, 'invert_opt')

hasattrTruebağlantı nesnesinin invert_opttanımlanmış bir işlevi varsa döner . İşte otlatmanız için belgeler

https://docs.python.org/2/library/functions.html#hasattr https://docs.python.org/3/library/functions.html#hasattr


5
Kod takdir edilmekle birlikte, her zaman beraberinde bir açıklaması olmalıdır. Bu uzun olmak zorunda değil ama bekleniyor.
peterh - Monica'yı eski durumuna getir

iyi bir haber,
canınızı yakmasa

5
Bu, bağlantının bir özniteliği varsa True da döndürür connection.invert_opt = 'foo'.
Robert Hönig

20

Fonksiyonun tanımlanıp tanımlanmadığını kontrol etmenin bir istisna yakalamaktan daha hızlı bir yolu var mı?

Neden buna karşısın? Çoğu Pythonic vakasında af dilemek izin istemekten daha iyidir. ;-)

hasattr getattr aranarak ve yükselip yükselmediğine bakılarak gerçekleştirilir ki bu benim istediğim şey değil.

Yine, neden bu? Aşağıdakiler oldukça Pythonic:

    try:
        invert_op = self.invert_op
    except AttributeError:
        pass
    else:
        parent_inverse = invert_op(self.path.parent_op)
        ops.remove(parent_inverse)

Veya,

    # if you supply the optional `default` parameter, no exception is thrown
    invert_op = getattr(self, 'invert_op', None)  
    if invert_op is not None:
        parent_inverse = invert_op(self.path.parent_op)
        ops.remove(parent_inverse)

Bununla birlikte, bunun getattr(obj, attr, default)temelde bir istisna yakalanarak uygulandığına dikkat edin. Python ülkesinde bunda yanlış bir şey yok!


4

Buradaki yanıtlar, bir dizenin nesnenin bir özniteliğinin adı olup olmadığını kontrol eder. Özelliğin bir yöntem olup olmadığını kontrol etmek için fazladan bir adım (çağrılabilir kullanarak) gereklidir.

Bu nedenle, bir nesnenin bir öznitelik niteliğine sahip olup olmadığını kontrol etmenin en hızlı yolu nedir? Cevap

'attrib' in obj.__dict__

Bunun nedeni, bir diktenin anahtarlarını karma hale getirmesidir, bu nedenle anahtarın varlığını kontrol etmek hızlıdır.

Aşağıdaki zamanlama karşılaştırmalarına bakın.

>>> class SomeClass():
...         pass
...
>>> obj = SomeClass()
>>>
>>> getattr(obj, "invert_op", None)
>>>
>>> %timeit getattr(obj, "invert_op", None)
1000000 loops, best of 3: 723 ns per loop
>>> %timeit hasattr(obj, "invert_op")
The slowest run took 4.60 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 674 ns per loop
>>> %timeit "invert_op" in obj.__dict__
The slowest run took 12.19 times longer than the fastest. This could mean that an intermediate result is being cached.
10000000 loops, best of 3: 176 ns per loop

Bu, kullanan sınıflarda başarısız olur __slots__. __slots__öznitelik erişimini yaklaşık% 10 oranında hızlandırmaya yardımcı olur. stackoverflow.com/a/14119024/1459669
noɥʇʎԀʎzɐɹƆ

3

Nathan Ostgard'ın cevabını beğendim ve oy verdim. Ancak probleminizi çözmenin başka bir yolu, işlev çağrısının sonucunu önbelleğe alabilecek, hatırlatıcı bir dekoratör kullanmak olacaktır. Böylece devam edip bir şeyi çözen pahalı bir işleve sahip olabilirsiniz, ancak sonra onu tekrar tekrar aradığınızda sonraki aramalar hızlıdır; işlevin hafızaya alınmış versiyonu, bir diktedeki argümanları arar, asıl işlevin sonucu hesapladığı andaki diktedeki sonucu bulur ve sonucu hemen döndürür.

Raymond Hettinger'in "lru_cache" adlı hatırlatıcı bir dekoratör için bir reçete burada. Bunun bir sürümü artık Python 3.2'deki functools modülünde standarttır.

http://code.activestate.com/recipes/498245-lru-and-lfu-cache-decorators/

http://docs.python.org/release/3.2/library/functools.html


2

Python'daki herhangi bir şey gibi, yeterince uğraşırsan, cesaretini kırabilir ve gerçekten kötü bir şey yapabilirsin. Şimdi, kötü kısım şu:

def invert_op(self, op):
    raise NotImplementedError

def is_invert_op_implemented(self):
    # Only works in CPython 2.x of course
    return self.invert_op.__code__.co_code == 't\x00\x00\x82\x01\x00d\x00\x00S'

Lütfen bize bir iyilik yapın, sorunuzda ne varsa yapmaya devam edin ve PyPy ekibinde Python yorumlayıcısını hacklemediğiniz sürece bunu KULLANMAYIN . Orada sahip olduğunuz şey Pythonic, burada sahip olduğum şey saf KÖTÜ .


Yöntem herhangi bir istisna yaratırsa bu doğru olacaktır. co_namesEşit olup olmadığını da kontrol etmelisiniz ('NotImplementedError',). Bununla birlikte, bunun onu aşağı yukarı kötü mü yaptığından emin değilim.
kindall

1

Ayrıca sınıfın üzerinden geçebilirsiniz:

import inspect


def get_methods(cls_):
    methods = inspect.getmembers(cls_, inspect.isfunction)
    return dict(methods)

# Example
class A(object):
    pass

class B(object):
    def foo():
        print('B')


# If you only have an object, you can use `cls_ = obj.__class__`
if 'foo' in get_methods(A):
    print('A has foo')

if 'foo' in get_methods(B):
    print('B has foo')

0

__Dict__ özelliğindeki öznitelikleri kontrol etmek gerçekten hızlı olsa da, __dict__ karmasında görünmedikleri için bunu yöntemler için kullanamazsınız. Bununla birlikte, performans bu kadar kritikse, sınıfınızda bilgisayar korsanlığı çözümüne başvurabilirsiniz:

class Test():
    def __init__():
        # redefine your method as attribute
        self.custom_method = self.custom_method

    def custom_method(self):
        pass

Ardından yöntemi şu şekilde kontrol edin:

t = Test()
'custom_method' in t.__dict__

Şununla zaman karşılaştırması getattr:

>>%timeit 'custom_method' in t.__dict__
55.9 ns ± 0.626 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

>>%timeit getattr(t, 'custom_method', None)
116 ns ± 0.765 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

Bu yaklaşımı teşvik ettiğimden değil, ama işe yarıyor gibi görünüyor.

[DÜZENLE] Yöntem adı belirli bir sınıfta olmadığında performans artışı daha da yüksektir:

>>%timeit 'rubbish' in t.__dict__
65.5 ns ± 11 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

>>%timeit getattr(t, 'rubbish', None)
385 ns ± 12.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

1
__dict__geçersiz kılınabilir. Güvenilemez.
Xiao
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.