คุณสามารถแสดงรายการอาร์กิวเมนต์คำสำคัญที่ฟังก์ชันได้รับหรือไม่?


104

ฉันมีคำสั่งซึ่งฉันต้องส่งคีย์ / ค่าเป็นอาร์กิวเมนต์คำหลัก .. ตัวอย่างเช่น ..

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

สิ่งนี้ใช้งานได้ดีแต่ถ้ามีค่าใน d_args dict ที่ไม่ได้รับการยอมรับจาก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)

ฉันอาจจะแค่กรองคำสั่งจากรายการอาร์กิวเมนต์คำหลักที่ถูกต้อง แต่ฉันสงสัยว่า: มีวิธีแสดงรายการอาร์กิวเมนต์คำหลักโดยทางโปรแกรมที่ฟังก์ชันเฉพาะใช้หรือไม่?

คำตอบ:


151

สิ่งที่ดีกว่าการตรวจสอบวัตถุโค้ดโดยตรงและการหาตัวแปรคือการใช้โมดูลการตรวจสอบ

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

หากคุณต้องการทราบว่าสามารถเรียกได้ด้วยชุดของ args เฉพาะหรือไม่คุณต้องมี args ที่ไม่มีการระบุค่าเริ่มต้น เหล่านี้ได้โดย:

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)

ในทำนองเดียวกันในการตรวจสอบ args ที่ไม่ถูกต้องให้ใช้:

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)

(นี่เป็นสิ่งที่ดีสำหรับการแยกวิเคราะห์อาร์กิวเมนต์ของ python เท่านั้นรันไทม์ตรวจสอบค่าที่ไม่ถูกต้องใน kwargs ไม่สามารถตรวจพบได้อย่างชัดเจน)


ดี! ฉันไม่รู้จักฟังก์ชั่นนี้!
DzinX

1
เนื่องจากวิธีการใช้วัตถุรหัสมีความเหมือนกันมากหรือน้อยมีประโยชน์ที่จะต้องนำเข้าโมดูลเพิ่มอีกหนึ่งโมดูล ... ?
jmetz

@jmets - แน่นอนว่าการใช้โมดูลไลบรารีจะดีกว่าการม้วนของคุณเอง นอกจากนี้แอตทริบิวต์บนวัตถุรหัสยังมีความเป็นภายในมากกว่าและพร้อมสำหรับการเปลี่ยนแปลง (เช่นโปรดทราบว่าสิ่งนี้ถูกย้ายไปยังรหัสใน pyhon3) การใช้โมดูลเป็นอินเทอร์เฟซในอนาคตจะช่วยป้องกันคุณได้อีกเล็กน้อยในกรณีที่ภายในบางส่วนเหล่านี้เปลี่ยนแปลงไป นอกจากนี้ยังทำในสิ่งที่คุณอาจไม่เคยคิดเช่นโยนข้อผิดพลาดประเภทที่เหมาะสมในฟังก์ชันที่คุณไม่สามารถตรวจสอบได้ (เช่นฟังก์ชัน C)
Brian

13
inspect.getargspec(f)เลิกใช้แล้วตั้งแต่ Python 3.0; inspect.signature(f)วิธีการที่ทันสมัย
gerrit

FYI หากคุณต้องการรองรับ Cython และ Python วิธีนี้ใช้ไม่ได้กับฟังก์ชัน Cython'd ในทางกลับกัน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)

ตอนนี้คุณสามารถเรียกใช้ฟังก์ชันของคุณด้วย args ที่รู้จัก แต่แยก kwargs เช่น:

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

นี้อนุมานว่าfuncการใช้งานไม่มี*argsหรือ**kwargsเวทมนตร์ในลายเซ็นของ


จะเกิดอะไรขึ้นถ้าฉันต้องการพิมพ์เฉพาะคีย์ "คำหลัก" อาร์กิวเมนต์ "
เจีย

7

ใน Python 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

สำหรับโซลูชัน Python 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']

และในทำนองเดียวกันสำหรับ positionals var ใช้p.VAR_POSITIONALและคำหลัก var VAR_KEYWORDกับ

นอกจากนี้คุณสามารถเพิ่มประโยคคำสั่ง if เพื่อตรวจสอบว่ามีค่าเริ่มต้นหรือไม่โดยการตรวจสอบว่า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)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.