क्या आप एक फ़ंक्शन द्वारा प्राप्त कीवर्ड तर्कों को सूचीबद्ध कर सकते हैं?


104

मेरे पास एक तानाशाही है, जिसे मुझे कीवर्ड तर्क के रूप में कुंजी / मान पास करने की आवश्यकता है .. उदाहरण के लिए ..

d_args = {'kw1': 'value1', 'kw2': 'value2'}
example(**d_args)

यह ठीक काम करता है, लेकिन अगर d_args के ऐसे मान हैं जो exampleफ़ंक्शन द्वारा स्वीकार नहीं किए जाते हैं, तो यह स्पष्ट रूप से मर जाता है .. कहो, यदि उदाहरण फ़ंक्शन के रूप में परिभाषित किया गया हैdef example(kw2):

यह एक समस्या है क्योंकि मैं या तो पीढ़ी d_argsया exampleफ़ंक्शन को नियंत्रित नहीं करता हूं .. वे दोनों बाहरी मॉड्यूल से आते हैं, और exampleकेवल कुछ कीवर्ड-तर्कों को तानाशाही से स्वीकार करते हैं।

आदर्श रूप में मैं बस करूँगा

parsed_kwargs = feedparser.parse(the_url)
valid_kwargs = get_valid_kwargs(parsed_kwargs, valid_for = PyRSS2Gen.RSS2)
PyRSS2Gen.RSS2(**valid_kwargs)

मैं संभवत: मान्य कीवर्ड-तर्कों की एक सूची से केवल तानाशाह को फ़िल्टर करूंगा, लेकिन मैं सोच रहा था: क्या किसी विशिष्ट फ़ंक्शन को कीवर्ड तर्क को सूचीबद्ध करने का कार्यक्रम है?

जवाबों:


150

सीधे कोड ऑब्जेक्ट का निरीक्षण करने और चर बाहर काम करने से थोड़ा अच्छा है निरीक्षण मॉड्यूल का उपयोग करना।

>>> import inspect
>>> def func(a,b,c=42, *args, **kwargs): pass
>>> inspect.getargspec(func)
(['a', 'b', 'c'], 'args', 'kwargs', (42,))

यदि आप जानना चाहते हैं कि किसी विशेष सेट के साथ इसका कॉल करने योग्य है, तो आपको पहले से निर्दिष्ट डिफ़ॉल्ट के बिना आर्ग की आवश्यकता है। इनके द्वारा प्राप्त किया जा सकता है:

def getRequiredArgs(func):
    args, varargs, varkw, defaults = inspect.getargspec(func)
    if defaults:
        args = args[:-len(defaults)]
    return args   # *args and **kwargs are not required, so ignore them.

फिर एक समारोह यह बताने के लिए कि आप अपने विशेष कमांड से क्या गायब हैं:

def missingArgs(func, argdict):
    return set(getRequiredArgs(func)).difference(argdict)

इसी तरह, अमान्य आर्ग्स के लिए जाँच करने के लिए, उपयोग करें:

def invalidArgs(func, argdict):
    args, varargs, varkw, defaults = inspect.getargspec(func)
    if varkw: return set()  # All accepted
    return set(argdict) - set(args)

और अगर यह कॉल करने योग्य है तो एक पूर्ण परीक्षण:

def isCallableWithArgs(func, argdict):
    return not missingArgs(func, argdict) and not invalidArgs(func, argdict)

(यह केवल अजगर के अर्ग पार्सिंग के रूप में अच्छा है। kwargs में अमान्य मूल्यों के लिए कोई भी रनटाइम जाँच स्पष्ट रूप से पता नहीं लगाई जा सकती है।)


अच्छा! मैं इस समारोह को नहीं जानता था!
DzinX

1
यह देखते हुए कि कोड ऑब्जेक्ट का उपयोग करने का तरीका कम या ज्यादा समान है, क्या एक और मॉड्यूल आयात करने के लिए एक लाभ है ...?
जमैट

@ जेट्स - निश्चित रूप से - पुस्तकालय मॉड्यूल का उपयोग करने के लिए अपने स्वयं के रोल की तुलना में यह वास्तव में हमेशा बेहतर होता है। इसके अलावा, कोड ऑब्जेक्ट पर विशेषताएँ अधिक आंतरिक हैं, और परिवर्तन के लिए हैं (उदाहरण के लिए ध्यान दें कि यह pyhb3 में कोड में स्थानांतरित हो गया है)। इंटरफ़ेस के रूप में मॉड्यूल का उपयोग करने से भविष्य में आपको इनमें से कुछ इंटर्नल में कभी-कभी बदलाव होने की संभावना है। यह उन सामानों को भी करेगा जिन्हें आपने करने के लिए नहीं सोचा होगा, जैसे कि आप उन कार्यों पर एक उपयुक्त प्रकार की त्रुटि फेंक सकते हैं जिनका आप निरीक्षण नहीं कर सकते हैं (जैसे। सी फ़ंक्शन)।
ब्रायन

13
inspect.getargspec(f)पाइथन 3.0 के बाद से पदावनत किया गया है; आधुनिक विधि है inspect.signature(f)
Gerrit

FYI करें, यदि आप साइथन और पायथन का समर्थन करना चाहते हैं, तो यह विधि साइथॉन के कार्य पर काम नहीं करती है। co_varnamesविकल्प, दूसरे हाथ पर, दोनों में काम करता है।
partofthething

32

यह सभी निष्क्रिय तर्कों, कीवर्ड और गैर-कीवर्ड वाले नामों को प्रिंट करेगा:

def func(one, two="value"):
    y = one, two
    return y
print func.func_code.co_varnames[:func.func_code.co_argcount]

ऐसा इसलिए है क्योंकि पहले co_varnamesहमेशा पैरामीटर होते हैं (अगले स्थानीय चर हैं, जैसे yऊपर दिए गए उदाहरण में)।

तो अब आप एक समारोह हो सकता है:

def getValidArgs(func, argsDict):
    '''Return dictionary without invalid function arguments.'''
    validArgs = func.func_code.co_varnames[:func.func_code.co_argcount]
    return dict((key, value) for key, value in argsDict.iteritems() 
                if key in validArgs)

जो आप तब इस तरह इस्तेमाल कर सकते हैं:

>>> func(**getValidArgs(func, args))

संपादित करें : एक छोटा सा जोड़: यदि आपको वास्तव में किसी फ़ंक्शन के कीवर्ड तर्क की आवश्यकता है , तो आप func_defaultsउन्हें निकालने के लिए विशेषता का उपयोग कर सकते हैं:

def getValidKwargs(func, argsDict):
    validArgs = func.func_code.co_varnames[:func.func_code.co_argcount]
    kwargsLen = len(func.func_defaults) # number of keyword arguments
    validKwargs = validArgs[-kwargsLen:] # because kwargs are last
    return dict((key, value) for key, value in argsDict.iteritems() 
                if key in validKwargs)

अब आप अपने फ़ंक्शन को ज्ञात आर्ग्स के साथ कॉल कर सकते हैं, लेकिन निकाले गए kwargs, उदा:

func(param1, param2, **getValidKwargs(func, kwargsDict))

यह मानता है कि अपने हस्ताक्षर में funcकोई *argsया **kwargsजादू का उपयोग नहीं करता है ।


क्या होगा यदि मैं केवल "कीवर्ड" तर्क "कुंजी" प्रिंट करना चाहता हूं?
जिया

7

पायथन 3.0 में:

>>> import inspect
>>> import fileinput
>>> print(inspect.getfullargspec(fileinput.input))
FullArgSpec(args=['files', 'inplace', 'backup', 'bufsize', 'mode', 'openhook'],
varargs=None, varkw=None, defaults=(None, 0, '', 0, 'r', None), kwonlyargs=[], 
kwdefaults=None, annotations={})

7

एक पायथन 3 समाधान के लिए, आप उस तरह के मापदंडों केinspect.signature अनुसार उपयोग और फ़िल्टर कर सकते हैं जिनके बारे में आप जानना चाहते हैं।

स्थिति या कीवर्ड, कीवर्ड-ओनली, var पॉज़िशनल और var कीवर्ड पैरामीटर के साथ एक नमूना फ़ंक्शन लेना:

def spam(a, b=1, *args, c=2, **kwargs):
    print(a, b, args, c, kwargs)

आप इसके लिए एक हस्ताक्षर वस्तु बना सकते हैं:

from inspect import signature
sig =  signature(spam)

और फिर एक सूची के साथ फ़िल्टर करें जो आपको आवश्यक विवरणों का पता लगाने के लिए:

>>> # positional or keyword
>>> [p.name for p in sig.parameters.values() if p.kind == p.POSITIONAL_OR_KEYWORD]
['a', 'b']
>>> # keyword only
>>> [p.name for p in sig.parameters.values() if p.kind == p.KEYWORD_ONLY]
['c']

और, इसी तरह, उपयोग कर रहे हैं p.VAR_POSITIONALऔर साथ कीवर्ड के लिए var स्थिति के लिए VAR_KEYWORD

इसके अलावा, आप यदि कोई डिफ़ॉल्ट मान मौजूद है कि क्या p.defaultबराबर है या नहीं, यह जांचने के लिए आप एक खंड जोड़ सकते हैं p.empty


3

DzinX के उत्तर का विस्तार:

argnames = example.func_code.co_varnames[:func.func_code.co_argcount]
args = dict((key, val) for key,val in d_args.iteritems() if key in argnames)
example(**args)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.