SORU
29 EYLÜL 2008, PAZARTESİ


Dekore edilmiş fonksiyonların korunması imzaları

Bir şey çok genel bir dekoratör yazdım sanırım. Örneğin, belirli bir türü için tüm değişkenleri dönüştürmek, günlük gerçekleştirmek, memoization, vb uygulayabilir.

İşte bir örnek:

def args_as_ints(f):
    def g(*args, **kwargs):
        args = [int(x) for x in args]
        kwargs = dict((k, int(v)) for k, v in kwargs.items())
        return f(*args, **kwargs)
    return g

@args_as_ints
def funny_function(x, y, z=3):
    """Computes x*y   2*z"""
    return x*y   2*z

>>> funny_function("3", 4.0, z="5")
22

Şimdiye kadar her şey güzel. Bir sorun, ancak var. Süslü işlevi özgün işlevi belgelerine korumaz:

>>> help(funny_function)
Help on function g in module __main__:

g(*args, **kwargs)

Neyse ki, bir çözüm vardır:

def args_as_ints(f):
    def g(*args, **kwargs):
        args = [int(x) for x in args]
        kwargs = dict((k, int(v)) for k, v in kwargs.items())
        return f(*args, **kwargs)
    g.__name__ = f.__name__
    g.__doc__ = f.__doc__
    return g

@args_as_ints
def funny_function(x, y, z=3):
    """Computes x*y   2*z"""
    return x*y   2*z

Bu sefer, işlev adı ve belgeler doğru

>>> help(funny_function)
Help on function funny_function in module __main__:

funny_function(*args, **kwargs)
    Computes x*y   2*z

Ama orada hala bir sorun: işlev imzası yanlış. Bilgiler ", ***kwargs besten" gelecek için bir işe yaramaz.

Ne? Basit ama mükemmel olmayan iki geçici çözüm düşünebilirsiniz:

1 -- Fibonacci sayıları doğru imza Vardır:

def funny_function(x, y, z=3):
    """funny_function(x, y, z=3) -- computes x*y   2*z"""
    return x*y   2*z

Bu tekrarından dolayı kötü. İmza hala düzgün bir şekilde otomatik olarak oluşturulan belgelerinde gösterilecektir. Kolay işlevi güncelleme ve Fibonacci sayıları değişen, ya da bir yazım hatası yapmak için unutmak. [ . ben^>Ve evet, Fibonacci sayıları zaten işlev gövdesi çoğaltan bu gerçeğin farkındayım. Lütfen bu; funny_function rasgele bir örnek, sadece görmezden.]

2 -- bir dekoratör kullanın, ya da her özel imza için özel amaçlı bir dekoratör kullanın:

def funny_functions_decorator(f):
    def g(x, y, z=3):
        return f(int(x), int(y), z=int(z))
    g.__name__ = f.__name__
    g.__doc__ = f.__doc__
    return g

Bu aynı imza olan fonksiyonları bir dizi için gayet iyi çalışıyor, ama genelde işe yaramaz. Başlangıçta da söylediğim gibi, dekoratörler tamamen kullanabilmek için jenerik olmak istiyorum.

Tamamen genel ve otomatik bir çözüm arıyorum.

Soru şu: orada bir şekilde oluşturulduktan sonra? dekore işlev imzası düzenlemek için.

Aksi takdirde, işlev imzası ayıklar ve bu bilgileri kullanan bir dekoratör yerine "*kwargs, **kwargs" dekore edilmiş fonksiyon inşa? yazabilir miyim Bu nasıl bilgi elde edebilirim? Nasıl dekore işlevi exec ile inşa etmeli miyim?

Başka yaklaşımlar?

CEVAP
29 EYLÜL 2008, PAZARTESİ


  1. decorator modülü yükleyin:

    $ pip install decorator
    
  2. Uyum args_as_ints() tanımı:

    import decorator
    
    @decorator.decorator
    def args_as_ints(f, *args, **kwargs):
        args = [int(x) for x in args]
        kwargs = dict((k, int(v)) for k, v in kwargs.items())
        return f(*args, **kwargs)
    
    @args_as_ints
    def funny_function(x, y, z=3):
        """Computes x*y   2*z"""
        return x*y   2*z
    
    print funny_function("3", 4.0, z="5")
    # 22
    help(funny_function)
    # Help on function funny_function in module __main__:
    # 
    # funny_function(x, y, z=3)
    #     Computes x*y   2*z
    

Python 3.4

functools.wraps() from stdlib Python 3.4 beri: imza korur

import functools


def args_as_ints(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        args = [int(x) for x in args]
        kwargs = dict((k, int(v)) for k, v in kwargs.items())
        return func(*args, **kwargs)
    return wrapper


@args_as_ints
def funny_function(x, y, z=3):
    """Computes x*y   2*z"""
    return x*y   2*z


print(funny_function("3", 4.0, z="5"))
# 22
help(funny_function)
# Help on function funny_function in module __main__:
#
# funny_function(x, y, z=3)
#     Computes x*y   2*z

functools.wraps() imzası var korumaz at least since Python 2.5 ama kullanılabilir:

help(funny_function)
# Help on function funny_function in module __main__:
#
# funny_function(*args, **kwargs)
#    Computes x*y   2*z

Dikkat: *args, **kwargs yerine x, y, z=3.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Ampisound

    Ampisound

    12 Kasım 2006
  • MagicofRahat

    MagicofRahat

    13 Temmuz 2007
  • MikeyMacintosh

    MikeyM

    28 Aralık 2009