Yöntem parametre adları nasıl alınır?


236

Python işlevi verildiğinde:

def a_method(arg1, arg2):
    pass

Bağımsız değişkenlerin sayısını ve adlarını nasıl ayıklayabilirim. Yani, referansım olduğu için geri dönmek funcistiyorum .func.[something]("arg1", "arg2")

Bunun için kullanım senaryosu, bir dekoratör var ve yöntem bağımsız değişkenlerini gerçek işlev için bir anahtar olarak göründükleri sırayla kullanmak istiyorum. Yani, dekoratör "a,b"aradığımda basılan nasıl görünüyor a_method("a", "b")?


1
Neredeyse özdeş bir sorunun yanıtlarının farklı bir listesi için, bu diğer stackoverflow post
dan mackinlay

1
Başlığınız yanıltıcıdır: 'yöntem' kelimesi 'işlev' kelimesiyle söylendiğinde, genellikle bir sınıf yöntemi düşünülür. Fonksiyon için, seçtiğiniz cevap (Jouni K. Seppanen'den) iyidir. Ancak (sınıf) yöntem için işe yaramıyor ve inceleme çözümü (Brian'dan) kullanılmalıdır.
Juh_

Yanıtlar:


351

inspectModüle bir göz atın - bu sizin için çeşitli kod nesnesi özelliklerinin incelenmesini sağlayacaktır.

>>> inspect.getfullargspec(a_method)
(['arg1', 'arg2'], None, None, None)

Diğer sonuçlar * args ve ** kwargs değişkenlerinin adı ve verilen varsayılan değerlerdir. yani.

>>> def foo(a, b, c=4, *arglist, **keywords): pass
>>> inspect.getfullargspec(foo)
(['a', 'b', 'c'], 'arglist', 'keywords', (4,))

Bazı callables'ın Python'un bazı uygulamalarında iç içe geçemeyebileceğini unutmayın. Örneğin, CPython'da, C'de tanımlanan bazı yerleşik işlevler argümanları hakkında meta veri sağlamaz. Sonuç olarak, yerleşik bir işlevde ValueErrorkullanırsanız bir alırsınız inspect.getfullargspec().

Python 3.3'ten beri, inspect.signature()çağrılabilir bir nesnenin çağrı imzasını görmek için kullanabilirsiniz :

>>> inspect.signature(foo)
<Signature (a, b, c=4, *arglist, **keywords)>

29
Kod, varsayılan (4,)parametrenin cözellikle anahtar kelime parametresine karşılık geldiğini nasıl bilebilir ?
fatuhoku

63
@fatuhoku Aynı şeyi merak ediyordum. Bitişik bir blokta yalnızca varsayılan bağımsız değişkenler ekleyebileceğiniz için belirsiz olmadığı ortaya çıkıyor. Dokümanlardan: "Eğer bu demet n öğeye sahipse, argümanlarda listelenen son n öğeye karşılık gelir"
Soverman

9
Bence Python 3.x getargspec (...) yerine inspector.signature (func)
Diego Andrés Díaz Espinoza

2
2.6 sürümünde değiştirildi: Adlandırılmış bir ArgSpec grubunu (argümanlar, varargs, anahtar kelimeler, varsayılanlar) döndürür.
öğretim üyesi

4
Bu doğru, @ DiegoAndrésDíazEspinoza - Python 3'te, inspect.getargspeckullanımdan kaldırıldı , ancak değiştirme inspect.getfullargspec.
j08lue

100

CPython'da, bağımsız değişken sayısı

a_method.func_code.co_argcount

ve isimleri

a_method.func_code.co_varnames

Bunlar CPython'un uygulama detaylarıdır, bu nedenle bu muhtemelen Python'un IronPython ve Jython gibi diğer uygulamalarında çalışmaz.

"Doğrudan geçiş" bağımsız değişkenlerini kabul etmenin taşınabilir yollarından biri, işlevinizi imza ile tanımlamaktır func(*args, **kwargs). Bu, dış API katmanının alt düzey API'ye çok sayıda anahtar kelime argümanı geçirdiği örneğin matplotlib'de çok kullanılır .


co_varnames standart Python ile çalışır, ancak dahili argümanları da görüntüleyeceği için bu yöntem tercih edilmez.
MattK

11
Neden aMethod.func_code.co_varnames [: aMethod.func_code.co_argcount] kullanmıyorsunuz?
hochl

Sonra argümanlarla çalışmıyor *args, örneğin:def foo(x, *args, y, **kwargs): # foo.__code__.co_argcount == 1
Nikolay Makhalin


Lütfen bunun yerine inspect kullanın. Aksi takdirde, kodunuz functools.wraps ile 3.4+ sürümünde iyi çalışmaz. Bkz. Stackoverflow.com/questions/147816/…
Brian McCutchon

22

Bir dekoratör yönteminde, orijinal yöntemin argümanlarını şu şekilde listeleyebilirsiniz:

import inspect, itertools 

def my_decorator():

        def decorator(f):

            def wrapper(*args, **kwargs):

                # if you want arguments names as a list:
                args_name = inspect.getargspec(f)[0]
                print(args_name)

                # if you want names and values as a dictionary:
                args_dict = dict(itertools.izip(args_name, args))
                print(args_dict)

                # if you want values as a list:
                args_values = args_dict.values()
                print(args_values)

Eğer **kwargssizin için önemliyse, o zaman biraz karmaşık olacaktır:

        def wrapper(*args, **kwargs):

            args_name = list(OrderedDict.fromkeys(inspect.getargspec(f)[0] + kwargs.keys()))
            args_dict = OrderedDict(list(itertools.izip(args_name, args)) + list(kwargs.iteritems()))
            args_values = args_dict.values()

Misal:

@my_decorator()
def my_function(x, y, z=3):
    pass


my_function(1, y=2, z=3, w=0)
# prints:
# ['x', 'y', 'z', 'w']
# {'y': 2, 'x': 1, 'z': 3, 'w': 0}
# [1, 2, 3, 0]

Bu cevap kısmen eskimiş ve güncellenmesi gerekiyor.
Imago

15

Bence aradığın yerliler metodu -


In [6]: def test(a, b):print locals()
   ...: 

In [7]: test(1,2)              
{'a': 1, 'b': 2}

7
Bu, burada ilgilenilen bağlam (dekoratör) dışında bir işlev dışında işe yaramaz.
Piotr Dobrogost

8
Aslında tam olarak aradığım şey, buradaki sorunun cevabı olmasa da.
javabeangrinder

15

Python 3 sürümü:

def _get_args_dict(fn, args, kwargs):
    args_names = fn.__code__.co_varnames[:fn.__code__.co_argcount]
    return {**dict(zip(args_names, args)), **kwargs}

Yöntem, hem args hem de kwargs içeren bir sözlük döndürür.


[:fn.__code__.co_argcount]İşlev bağımsız değişkenlerini arıyorsanız bunun çok önemli olduğuna dikkat edin - aksi takdirde işlev içinde oluşturulan adları da içerir.
Soren Bjornstad

13

İşte bir dekoratör kullanarak istediğiniz şey için çalışacağını düşündüğüm bir şey.

class LogWrappedFunction(object):
    def __init__(self, function):
        self.function = function

    def logAndCall(self, *arguments, **namedArguments):
        print "Calling %s with arguments %s and named arguments %s" %\
                      (self.function.func_name, arguments, namedArguments)
        self.function.__call__(*arguments, **namedArguments)

def logwrap(function):
    return LogWrappedFunction(function).logAndCall

@logwrap
def doSomething(spam, eggs, foo, bar):
    print "Doing something totally awesome with %s and %s." % (spam, eggs)


doSomething("beans","rice", foo="wiggity", bar="wack")

Çalıştırın, aşağıdaki çıktıyı verecektir:

C:\scripts>python decoratorExample.py
Calling doSomething with arguments ('beans', 'rice') and named arguments {'foo':
 'wiggity', 'bar': 'wack'}
Doing something totally awesome with beans and rice.

11

Python 3.5+:

DeprecationWarning: inspect.getargspec () kullanımdan kaldırıldı, bunun yerine inspect.signature () kullanın

Daha önce:

func_args = inspect.getargspec(function).args

Şimdi:

func_args = list(inspect.signature(function).parameters.keys())

Test etmek için:

'arg' in list(inspect.signature(function).parameters.keys())

'Arg' argümanını alan 'function' fonksiyonuna sahip olduğumuz göz önüne alındığında, bu True olarak değerlendirilir, aksi halde False olarak değerlendirilir.

Python konsolundan örnek:

Python 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 07:18:10) [MSC v.1900 32 bit (Intel)] on win32
>>> import inspect
>>> 'iterable' in list(inspect.signature(sum).parameters.keys())
True

8

Python 3. + 'da Signatureelinizdeki nesneyle, argüman adları ile değerlerin eşleştirilmesini sağlamanın kolay bir yolu İmzanın bind()yöntemini kullanmaktır!

Örneğin, şöyle bir harita yazdırmak için bir dekoratör:

import inspect

def decorator(f):
    def wrapper(*args, **kwargs):
        bound_args = inspect.signature(f).bind(*args, **kwargs)
        bound_args.apply_defaults()
        print(dict(bound_args.arguments))

        return f(*args, **kwargs)

    return wrapper

@decorator
def foo(x, y, param_with_default="bars", **kwargs):
    pass

foo(1, 2, extra="baz")
# This will print: {'kwargs': {'extra': 'baz'}, 'param_with_default': 'bars', 'y': 2, 'x': 1}

6

Herhangi bir modül kullanmadan fonksiyon parametrelerini almanın başka bir yolu.

def get_parameters(func):
    keys = func.__code__.co_varnames[:func.__code__.co_argcount][::-1]
    sorter = {j: i for i, j in enumerate(keys[::-1])} 
    values = func.__defaults__[::-1]
    kwargs = {i: j for i, j in zip(keys, values)}
    sorted_args = tuple(
        sorted([i for i in keys if i not in kwargs], key=sorter.get)
    )
    sorted_kwargs = {}
    for i in sorted(kwargs.keys(), key=sorter.get):
        sorted_kwargs[i] = kwargs[i]      
    return sorted_args, sorted_kwargs


def f(a, b, c="hello", d="world"): var = a


print(get_parameters(f))

Çıktı:

(('a', 'b'), {'c': 'hello', 'd': 'world'})

2

Bağımsız değişken adlarının bir listesini döndürür, kısmi ve normal işlevlerle ilgilenir:

def get_func_args(f):
    if hasattr(f, 'args'):
        return f.args
    else:
        return list(inspect.signature(f).parameters)

2

Brian'ın cevabı için güncelleme :

Python 3'teki bir işlevde yalnızca anahtar kelime bağımsız değişkenleri varsa, şunu kullanmanız gerekir inspect.getfullargspec:

def yay(a, b=10, *, c=20, d=30):
    pass
inspect.getfullargspec(yay)

bunu verir:

FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=(10,), kwonlyargs=['c', 'd'], kwonlydefaults={'c': 20, 'd': 30}, annotations={})

2

Python 3'te, aşağıdakileri yapmak *argsve **kwargsiçine dict( siparişleri OrderedDictkorumak için python <3.6 için kullanın dict):

from functools import wraps

def display_param(func):
    @wraps(func)
    def wrapper(*args, **kwargs):

        param = inspect.signature(func).parameters
        all_param = {
            k: args[n] if n < len(args) else v.default
            for n, (k, v) in enumerate(param.items()) if k != 'kwargs'
        }
        all_param .update(kwargs)
        print(all_param)

        return func(**all_param)
    return wrapper

1

inspect.signatureçok yavaş. En hızlı yol

def f(a, b=1, *args, c, d=1, **kwargs):
   pass

f_code = f.__code__
f_code.co_varnames[:f_code.co_argcount + f_code.co_kwonlyargcount]  # ('a', 'b', 'c', 'd')

0

Biraz ısırdı güncellemek için Brian'ın cevabı , şimdi güzel bir backport var inspect.signatureEski piton sürümlerinde kullanabileceğiniz: funcsigs. Bu yüzden kişisel tercihim

try:  # python 3.3+
    from inspect import signature
except ImportError:
    from funcsigs import signature

def aMethod(arg1, arg2):
    pass

sig = signature(aMethod)
print(sig)

Eğlenmek için, Signaturenesnelerle oynamak ve hatta rastgele imzalarla dinamik olarak işlevler oluşturmak istiyorsanız, makefunprojeme bir göz atabilirsiniz .


-3

Ne hakkında dir()ve vars()şimdi?

Süper basit bir şekilde sorulan şeyi yapıyor gibi görünüyor…

İşlev kapsamından çağrılmalıdır.

Ancak tüm yerel değişkenleri döndüreceğinden emin olun, bu yüzden gerekirse işlevin en başında yaptığınızdan emin olun.

Ayrıca, yorumlarda belirtildiği gibi, bunun kapsamın dışından yapılmasına izin vermediğini unutmayın. Yani OP'nin senaryosu tam olarak değil, yine de soru başlığıyla eşleşiyor. Bu yüzden cevabım.


dir () tüm değişken adlarının listesini döndürür ['var1', 'var2'], vars () sözlüğü geçerli yerel kapsamdan {'var1': 0, 'var2': 'bir şey'} biçiminde döndürür. Birisi işlevde daha sonra bağımsız değişken değişken adlarını kullanmak istiyorsa, başka bir yerel değişkene kaydetmelidir, çünkü daha sonra başka bir yerel değişken bildirebilecekleri işlevde çağırmak bu listeyi "kirletecektir". Fonksiyonun dışında kullanmak istedikleri takdirde, fonksiyonu en az bir kez çalıştırmalı ve global değişkene kaydetmelidirler. Bu nedenle denetleme modülünü kullanmak daha iyidir.
Peter Majko
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.