ฉันจะหาจำนวนอาร์กิวเมนต์ของฟังก์ชัน Python ได้อย่างไร


163

ฉันจะหาจำนวนอาร์กิวเมนต์ของฟังก์ชัน Python ได้อย่างไร ฉันจำเป็นต้องรู้ว่ามีข้อโต้แย้งปกติกี่ข้อและมีการตั้งชื่อข้อโต้แย้งกี่ข้อ

ตัวอย่าง:

def someMethod(self, arg1, kwarg1=None):
    pass

วิธีนี้มี 2 ข้อโต้แย้งและ 1 ชื่ออาร์กิวเมนต์


43
คำถามที่รับประกันอย่างเต็มที่; หากไม่เป็นเช่นนั้น (เนื่องจากคุณสามารถอ่านซอร์สได้เสมอ) จะไม่มีเหตุผลใด ๆ สำหรับinspectโมดูลไลบรารีมาตรฐาน
flow

ภาษาจำนวนมากมีคุณสมบัติที่ไม่ยุติธรรมอย่างน้อยหนึ่งรายการ inspectโมดูลมีจำนวนมากของคุณสมบัติอื่น ๆ ดังนั้นจึงไม่เป็นธรรมที่จะกล่าวว่าโมดูลทั้งจะไม่ยุติธรรมถ้าฟังก์ชั่นหนึ่งในนั้นคือ ยิ่งไปกว่านั้นมันง่ายที่จะเห็นว่าคุณลักษณะนี้สามารถใช้งานได้ไม่ดี (ดูstackoverflow.com/questions/741950 ) ที่กล่าวว่ามันเป็นคุณสมบัติที่มีประโยชน์โดยเฉพาะอย่างยิ่งสำหรับการเขียนตกแต่งและฟังก์ชั่นอื่น ๆ ที่ทำงานในฟังก์ชั่น
user1612868

คำตอบ:


131

คำตอบที่ได้รับการยอมรับก่อนหน้านี้ได้รับการเลิกPython 3.0ณ แทนที่จะใช้inspect.getargspecคุณควรเลือกSignatureชั้นเรียนที่แทนที่

การสร้างลายเซ็นสำหรับฟังก์ชั่นนั้นง่ายผ่านsignatureฟังก์ชั่น :

from inspect import signature

def someMethod(self, arg1, kwarg1=None):
    pass

sig = signature(someMethod)

ตอนนี้คุณสามารถดูพารามิเตอร์ได้อย่างรวดเร็วโดยการนำstrเข้าไปที่:

str(sig)  # returns: '(self, arg1, kwarg1=None)'

sig.parametersหรือคุณยังสามารถได้รับการทำแผนที่ของชื่อแอตทริบิวต์วัตถุพารามิเตอร์ผ่าน

params = sig.parameters 
print(params['kwarg1']) # prints: kwarg1=20

นอกจากนี้คุณสามารถโทรlenได้ที่sig.parametersจะเห็นจำนวนของอาร์กิวเมนต์ฟังก์ชั่นนี้ต้องใช้:

print(len(params))  # 3

แต่ละรายการในการparamsทำแผนที่เป็นParameterวัตถุที่มีคุณสมบัติเพิ่มเติมทำให้ชีวิตของคุณง่ายขึ้น ตัวอย่างเช่นการคว้าพารามิเตอร์และการดูค่าเริ่มต้นทำได้ง่าย ๆ ด้วย:

kwarg1 = params['kwarg1']
kwarg1.default # returns: None

parametersในทำนองเดียวกันสำหรับส่วนที่เหลือของวัตถุที่มีอยู่ใน


สำหรับ2.xผู้ใช้Python แม้ว่าinspect.getargspec จะไม่ได้เลิกใช้ภาษาก็จะเป็น :-) Signatureระดับคือไม่สามารถใช้ได้ใน2.xชุดและจะไม่ inspect.getargspecดังนั้นคุณยังคงต้องทำงานร่วมกับ

สำหรับการเปลี่ยนระหว่างงูหลาม 2 และ 3 ถ้าคุณมีรหัสที่อาศัยอยู่กับอินเตอร์เฟซของgetargspecธ ที่ 2 และการเปลี่ยนไปsignatureใน3เป็นเรื่องยากเกินไปที่คุณจะมีตัวเลือกที่มีคุณค่าinspect.getfullargspecของการใช้ มันมีอินเทอร์เฟซที่คล้ายกันกับgetargspec(อาร์กิวเมนต์ที่เรียกได้เพียงครั้งเดียว) เพื่อที่จะคว้าอาร์กิวเมนต์ของฟังก์ชันในขณะที่จัดการกับกรณีเพิ่มเติมบางอย่างที่getargspecไม่ได้:

from inspect import getfullargspec

def someMethod(self, arg1, kwarg1=None):
    pass

args = getfullargspec(someMethod)

เช่นเดียวกับgetargspec, getfullargspecส่งกลับNamedTupleซึ่งมีข้อโต้แย้ง

print(args)
FullArgSpec(args=['self', 'arg1', 'kwarg1'], varargs=None, varkw=None, defaults=(None,), kwonlyargs=[], kwonlydefaults=None, annotations={})

1
ยินดีต้อนรับคุณ @ GeorgSchölly ฉันรู้สึกประหลาดใจกับคำถามยอดนิยมเช่นนี้ที่นำเสนอโซลูชันที่เลิกใช้แล้วหรือถูกลับๆล่อๆ (ดูที่co_argcountคุณสมบัติ)
Dimitris Fasarakis Hilliard

1
getfullargspecไม่ได้ใช้งานใน Python 2.x คุณต้องใช้getargspec
Peter Gibson

getargspecใช้งานไม่ได้กับฟังก์ชั่นในตัว: getargspec(open)ให้TypeError: <built-in function open> is not a Python functionดูคำตอบนี้สำหรับแนวคิดบางอย่าง ...
starfry

ในตัวอย่างล่าสุดของคุณเมื่อคุณทำprint(args)คุณไม่ได้รับคุณจะได้รับdefaults=(None,) defaults=None
Seanny123

มีวิธีรับค่าพารามิเตอร์ดั้งเดิมสำหรับฟังก์ชั่นที่ได้รับการตกแต่งแล้วหรือไม่?
Gulats

117
import inspect
inspect.getargspec(someMethod)

ดูโมดูลตรวจสอบ


5
โดยทั่วไปสิ่งที่คุณต้องการ แต่สิ่งนี้ไม่ได้ผลกับฟังก์ชั่นในตัว วิธีเดียวที่จะทราบว่าจะได้รับข้อมูลนี้สำหรับบิวด์อินคือการแยกวิเคราะห์สตริงเอกสารของพวกเขาซึ่งเป็นที่น่าเกลียด แต่ทำได้
Cerin

5
สิ่งนี้เลิกใช้แล้วใน Python 3: docs.python.org/3/library/inspect.html#inspect.getargspec
noisecapella

มีวิธีแก้ปัญหาที่ไม่เลิกใช้ใน Python 3 หรือไม่?
hlin117

1
หากคุณไปที่ลิงก์คุณจะเห็นว่ามันแนะนำให้ใช้inspect.signature- docs.python.org/3/library/inspect.html#inspect.signature
coderforlife

ฉันโพสต์วิธีการที่เป็นไปได้อีกอย่างสำหรับฟังก์ชั่นบิวอินโดยไม่ต้องแยกวิเคราะห์ docstring ที่นี่: stackoverflow.com/questions/48567935/…
HelloWorld

31
someMethod.func_code.co_argcount

หรือถ้าชื่อฟังก์ชั่นปัจจุบันไม่ได้ระบุไว้:

import sys

sys._getframe().func_code.co_argcount

4
@elyase เพียงแค่ทำ: dir(someMethod)-> 'func_code'; ไปต่อ: dir(someMethod.func_code)-> 'co_argcount'; คุณสามารถใช้บิวด์อินdir()เพื่อกำหนดวิธีการที่มีอยู่ของวัตถุ

@elyase ฉันก็เป็นคนที่เหมือนกันดังนั้นฉันจึงพบdocs.python.org/2/library/inspect.html#types-and-members
rodripf

เพื่อรองรับ Python 3:six.get_function_code(someMethod).co_argcount
noisecapella

8
@ noisecapella ไม่จำเป็นต้องมีโมดูลของบุคคลที่สามเมื่อคุณสามารถทำได้some_method.__code__.co_argcount
vallentin

1
โดยทั่วไปแล้วคุณไม่ควรมองเข้าไปในวัตถุฟังก์ชั่นเพื่อดูสิ่งเหล่านี้ co_argcountจะใช้ภายในระหว่างการประเมินผลของวัตถุรหัส สิ่งที่ฉันพยายามจะพูดก็คือไม่มีการรับประกันว่าคุณลักษณะเหล่านี้จะไม่เปลี่ยนจากรุ่นหนึ่งเป็นรุ่นอื่น
Dimitris Fasarakis Hilliard

16

inspect.getargspec ()

รับชื่อและค่าเริ่มต้นของอาร์กิวเมนต์ของฟังก์ชัน ทูเปิลสี่สิ่งถูกส่งคืน: (args, varargs, varkw, ค่าเริ่มต้น) args เป็นรายการชื่ออาร์กิวเมนต์ (มันอาจมีรายการซ้อนกัน) varargs และ varkw เป็นชื่อของอาร์กิวเมนต์ * และ ** หรือไม่มี ค่าเริ่มต้นเป็น tuple ของค่าอาร์กิวเมนต์เริ่มต้นหรือไม่มีถ้าไม่มีข้อโต้แย้งเริ่มต้น; หาก tuple นี้มีองค์ประกอบ n พวกเขาสอดคล้องกับองค์ประกอบ n ล่าสุดที่ระบุไว้ใน args

เปลี่ยนเป็นเวอร์ชั่น 2.6: ส่งคืน tuple ที่ชื่อ ArgSpec (args, varargs, คำหลัก, ค่าเริ่มต้น)

ดูCan-คุณรายการที่คำหลักข้อโต้แย้ง-a-หลามฟังก์ชั่นได้รับ


5

นอกจากนี้ฉันยังเห็นด้วยว่าฟังก์ชัน help () ส่วนใหญ่ช่วยได้จริงๆ

ตัวอย่างเช่นมันให้รายละเอียดทั้งหมดเกี่ยวกับข้อโต้แย้งที่ต้องใช้

help(<method>)

ให้ด้านล่าง

method(self, **kwargs) method of apiclient.discovery.Resource instance
Retrieves a report which is a collection of properties / statistics for a specific customer.

Args:
  date: string, Represents the date in yyyy-mm-dd format for which the data is to be fetched. (required)
  pageToken: string, Token to specify next page.
  parameters: string, Represents the application name, parameter name pairs to fetch in csv as app_name1:param_name1, app_name2:param_name2.

Returns:
  An object of the form:

    { # JSON template for a collection of usage reports.
    "nextPageToken": "A String", # Token for retrieving the next page
    "kind": "admin#reports#usageReports", # Th

มันจะดีสำหรับคนที่จะแสดงความคิดเห็นในสิ่งที่ผิดกับการโพสต์มากกว่าเพียงแค่คลิกที่ปุ่มลบ
Venu Murthy

2
helpฟังก์ชั่นแสดงเฉพาะสิ่งที่ docstring พูดว่า คุณเคยทดสอบว่ามันใช้ได้กับฟังก์ชั่นนิยามในคำถามหรือไม่?
0xc0de

@ 0xc0de - คุณทดสอบแล้วหรือยัง เพราะมันใช้งานได้จริง help()แยกออกมากกว่า docstring - แม้ในรหัสที่ไม่มีเอกสารมันยังพิมพ์ออกมา argspec และบอกคุณว่ารหัสถูกกำหนด คนที่โพสต์คำถามดั้งเดิมไม่ชัดเจนว่าพวกเขาต้องการคำตอบที่เป็นมิตรกับมนุษย์หรือไม่ หากจำเป็นต้องเป็นมิตรกับมนุษย์help()ก็เพียงพอแล้ว
ArtOfWarfare

@ArtOfWarfare ไม่ได้ทั้งหมดเป็นตอนนี้คุณจะต้องแยกสิ่งhelp()ตอบแทนและพยายามหาสิ่งที่และargs kwargs
vallentin

5

ข่าวดีสำหรับคนที่ต้องการทำแบบพกพาระหว่าง Python 2 และ Python 3.6+: inspect.getfullargspec()วิธีการใช้งาน มันทำงานได้ทั้ง Python 2.x และ 3.6+

ดังที่ Jim Fasarakis Hilliard และคนอื่น ๆ ชี้ให้เห็นมันเคยเป็นเช่นนี้:
1 ใน Python 2.x: ใช้inspect.getargspec()
2 ใน Python 3.x: ใช้ลายเซ็นเหมือนgetargspec()และgetfullargspec()เลิกใช้แล้ว

อย่างไรก็ตามการเริ่มต้น Python 3.6 (ตามความต้องการที่เป็นที่นิยม?) สิ่งต่าง ๆ ได้เปลี่ยนไปสู่สิ่งที่ดีกว่า:

จากหน้าเอกสาร Python 3 :

inspect.getfullargspec (func)

เปลี่ยนเป็นเวอร์ชั่น 3.6 : ก่อนหน้านี้วิธีการนี้ได้รับการบันทึกไว้ว่าไม่สนับสนุนsignature()ใน Python 3.5 แต่การตัดสินใจนั้นกลับด้านเพื่อกู้คืนอินเทอร์เฟซมาตรฐานที่รองรับอย่างชัดเจนสำหรับรหัส Python 2/3 ต้นทางเดียวที่โยกย้ายออกจากgetargspec()API ดั้งเดิม


3

inspect.getargspec () เพื่อตอบสนองความต้องการของคุณ

from inspect import getargspec

def func(a, b):
    pass
print len(getargspec(func).args)

1
ยินดีต้อนรับสู่ Stack Overflow! กรุณาอย่าตอบเพียงแค่มีซอร์สโค้ด พยายามให้คำอธิบายที่ดีเกี่ยวกับวิธีการแก้ปัญหาของคุณ ดู: ฉันจะเขียนคำตอบที่ดีได้อย่างไร . ขอบคุณ
sɐunıɔןɐqɐp

2

ตามที่คำตอบอื่น ๆ แนะนำให้ใช้getargspecงานได้ดีตราบใดที่สิ่งที่ถูกสอบถามนั้นเป็นฟังก์ชั่น มันไม่ทำงานสำหรับในตัวฟังก์ชั่นเช่นopen, lenฯลฯ และจะโยนข้อยกเว้นในกรณีดังกล่าว:

TypeError: <built-in function open> is not a Python function

ฟังก์ชั่นด้านล่าง (แรงบันดาลใจจากคำตอบนี้ ) แสดงให้เห็นถึงวิธีการแก้ปัญหา ส่งคืนจำนวน args ที่คาดหวังโดยf:

from inspect import isfunction, getargspec
def num_args(f):
  if isfunction(f):
    return len(getargspec(f).args)
  else:
    spec = f.__doc__.split('\n')[0]
    args = spec[spec.find('(')+1:spec.find(')')]
    return args.count(',')+1 if args else 0

แนวคิดคือการแยกวิเคราะห์ข้อมูลจำเพาะของฟังก์ชันออกจาก__doc__สตริง เห็นได้ชัดว่าสิ่งนี้ขึ้นอยู่กับรูปแบบของสตริงที่กล่าวถึงดังนั้นจึงไม่ค่อยแข็งแกร่ง!


2

func.__code__.co_argcountช่วยให้คุณมีจำนวนข้อโต้แย้งใด ๆ ก่อน *args

func.__kwdefaults__ช่วยให้คุณกำหนดข้อโต้แย้งคำหลัก หลังจาก *args

func.__code__.co_kwonlyargcount เท่ากับ len(func.__kwdefaults__)

func.__defaults__ให้คุณค่ากับอาร์กิวเมนต์ที่เป็นตัวเลือกที่ปรากฏก่อนหน้านี้ *args

นี่คือภาพประกอบง่าย ๆ :

ภาพประกอบ

>>> def a(b, c, d, e, f=1, g=3, h=None, *i, j=2, k=3, **L):
    pass

>>> a.__code__.co_argcount
7
>>> a.__defaults__
(1, 3, None)
>>> len(a.__defaults__)
3
>>> 
>>> 
>>> a.__kwdefaults__
{'j': 2, 'k': 3}
>>> len(a.__kwdefaults__)
2
>>> a.__code__.co_kwonlyargcount
2

0

ใน:

import inspect 

class X:
    def xyz(self, a, b, c): 
        return 

print(len(inspect.getfullargspec(X.xyz).args))

ออก:

4


หมายเหตุ: หาก xyz ไม่ได้อยู่ใน class X และไม่มี "self" และเพียงแค่ "a, b, c" ก็จะมีการพิมพ์ 3

สำหรับงูหลามต่ำกว่า 3.5 คุณอาจต้องการแทนที่inspect.getfullargspecด้วยinspect.getargspecในรหัสข้างต้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.