سؤال

بالنظر إلى وظيفة بايثون:

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 ولكنه لا يزال يطابق عنوان السؤال.ومن هنا جوابي.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top