الحصول على أسماء معلمات الطريقة في بايثون
-
03-07-2019 - |
سؤال
بالنظر إلى وظيفة بايثون:
def aMethod(arg1, arg2):
pass
كيف يمكنني استخراج عدد وأسماء الحجج.على سبيل المثال، نظرًا لأن لدي إشارة إلى func، أريد أن يعود func.[something] ("arg1"، "arg2").
سيناريو الاستخدام لذلك هو أن لدي مصمم ديكور، وأرغب في استخدام وسائط الطريقة بنفس الترتيب الذي تظهر به للوظيفة الفعلية كمفتاح.على سبيل المثال، كيف سيبدو مصمم الديكور الذي طبع "a,b" عندما أسمي aMethod("a", "b")؟
المحلول
ونلقي نظرة على تفقد وحدة - وهذا سوف نفعل التفتيش على مختلف خصائص الكائن رمز لك.
>>> inspect.getfullargspec(aMethod)
(['arg1', 'arg2'], None, None, None)
والنتائج الأخرى هي اسم وسائط * و** المتغيرات kwargs، والافتراضات المقدمة. أي.
>>> def foo(a,b,c=4, *arglist, **keywords): pass
>>> inspect.getfullargspec(foo)
(['a', 'b', 'c'], 'arglist', 'keywords', (4,))
لاحظ أن بعض callables قد لا تكون introspectable في تطبيقات معينة من بيثون. على سبيل المثال، في سي بايثون، بعض المدمج في وظائف محددة في C لا توفر بيانات وصفية حول حججهم. ونتيجة لذلك، سوف تحصل ValueError
في حال كنت تستخدم inspect.getfullargspec()
مع وظيفة المضمنة.
ومنذ بايثون 3.3، يمكنك استخدام أيضا في inspect.signature () من أجل معرفة توقيع نداء كائن استدعاء:
>>> inspect.signature(foo)
<Signature (a, b, c=4, *arglist, **keywords)>
نصائح أخرى
في سي بايثون، وعدد من الحجج هو
aMethod.func_code.co_argcount
ووأسماء هم في بداية
aMethod.func_code.co_varnames
وهذه هي تفاصيل تنفيذ سي بايثون، لذلك هذا ربما لا يعمل في تطبيقات أخرى من بيثون، مثل و IronPython وجايثون.
وطريقة واحدة محمولة على الاعتراف "المار" الحجج هي تحديد وظيفة الخاص بك مع func(*args, **kwargs)
التوقيع. ويستخدم هذا كثيرا في مثل matplotlib، حيث يمر طبقة API الخارجية الكثير من الحجج الكلمة إلى API المستوى الأدنى.
في طريقة الديكور، يمكنك سرد الحجج من الأسلوب الأصلي بهذه الطريقة:
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)
وإذا كان **kwargs
هي مهمة بالنسبة لك، بعد ذلك سوف تكون معقدة بعض الشيء:
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()
مثال:
@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]
وهنا شيء وأعتقد أن العمل من أجل ما تريد، وذلك باستخدام الديكور.
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")
وتشغيله، وسوف تسفر عن الإخراج التالي:
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.
وأعتقد أن ما كنت تبحث عن طريقة السكان المحليين -
In [6]: def test(a, b):print locals()
...:
In [7]: test(1,2)
{'a': 1, 'b': 2}
والنسخة بيثون 3 هو:
def _get_args_dict(fn, args, kwargs):
args_names = fn.__code__.co_varnames[:fn.__code__.co_argcount]
return {**dict(zip(args_names, args)), **kwargs}
والأسلوب بإرجاع القاموس تحتوي على كل وسائط وkwargs.
وبيثون 3.5 +:
<اقتباس فقرة>وDeprecationWarning: inspect.getargspec () هو مستنكر، واستخدام inspect.signature () بدلا
اقتباس فقرة>وهكذا سابقا:
func_args = inspect.getargspec(function).args
والآن:
func_args = list(inspect.signature(function).parameters.keys())
لاختبار:
'arg' in list(inspect.signature(function).parameters.keys())
وبالنظر إلى أن لدينا وظيفة 'وظيفة' الذي يأخذ حجة "وسيطة"، وهذا تقييم صحيح كما، وإلا كما خطأ.
مثال من وحدة بيثون:
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
في بيثون 3. + مع الكائن Signature
في متناول اليد، وسيلة سهلة للحصول على تعيين بين أسماء حجة للقيم، يستخدم طريقة bind()
التوقيع المفضل
وعلى سبيل المثال، وهنا هو الديكور للطباعة خريطة من هذا القبيل:
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}
وإرجاع قائمة بأسماء حجة، يعتني جزئيات ووظائف العادية:
def get_func_args(f):
if hasattr(f, 'args'):
return f.args
else:
return list(inspect.signature(f).parameters)
إذا وظيفة في بيثون 3 ديها الكلمة فقط الحجج، فأنت بحاجة إلى استخدام inspect.getfullargspec
:
def yay(a, b=10, *, c=20, d=30):
pass
inspect.getfullargspec(yay)
وينتج هذا:
FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=(10,), kwonlyargs=['c', 'd'], kwonlydefaults={'c': 20, 'd': 30}, annotations={})
في بيثون 3 أدناه هو جعل *args
و**kwargs
إلى dict
(استخدام OrderedDict
لالثعبان <3.6 للحفاظ على أوامر 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
لتحديث قليلا قليلا براين الجواب ، هناك الآن backport لطيفة من inspect.signature
التي يمكنك استخدامها في بيثون كبار السن إصدارات: funcsigs
.
حتى بلدي تفضيل شخصي سيذهب ل
try: # python 3.3+
from inspect import signature
except ImportError:
from funcsigs import signature
def aMethod(arg1, arg2):
pass
sig = signature(aMethod)
print(sig)
لمتعة، وإذا كنت ترغب في اللعب مع كائنات Signature
وحتى خلق وظائف مع تواقيع عشوائية حيوي يمكنك إلقاء نظرة على بلدي <لأ href = "https://smarie.github.io/python-makefun/ "يختلط =" نوفولو noreferrer "> مشروع makefun
.
ماذا عن dir()
و vars()
الآن؟
يبدو أنه يفعل بالضبط ما يُطلب منه ببساطة فائقة ...
يجب استدعاؤه من داخل نطاق الوظيفة.
ولكن كن حذرا من أنه سيعود الجميع المتغيرات المحلية لذا تأكد من القيام بذلك في بداية الوظيفة إذا لزم الأمر.
لاحظ أيضًا أنه، كما هو مذكور في التعليقات، فإن هذا لا يسمح بالقيام بذلك من خارج النطاق.لذلك ليس بالضبط سيناريو OP ولكنه لا يزال يطابق عنوان السؤال.ومن هنا جوابي.