Bạn có thể liệt kê các đối số từ khóa mà một hàm nhận được không?


104

Tôi có một dict, tôi cần chuyển khóa / giá trị làm đối số từ khóa .. Ví dụ: ..

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

Điều này hoạt động tốt, nhưng nếu có các giá trị trong d_args dict không được examplehàm chấp nhận , nó rõ ràng sẽ chết .. Giả sử, nếu hàm ví dụ được định nghĩa làdef example(kw2):

Đây là một vấn đề vì tôi không kiểm soát việc tạo ra d_argshoặc examplehàm .. Cả hai đều đến từ các mô-đun bên ngoài và examplechỉ chấp nhận một số đối số từ khóa từ dict ..

Lý tưởng nhất là tôi sẽ làm

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

Tôi có thể sẽ chỉ lọc dict, từ danh sách các đối số từ khóa hợp lệ, nhưng tôi đã tự hỏi: Có cách nào để liệt kê theo chương trình các đối số từ khóa mà một hàm cụ thể sử dụng không?

Câu trả lời:


150

Tốt hơn một chút so với việc kiểm tra đối tượng mã trực tiếp và tìm ra các biến là sử dụng mô-đun kiểm tra.

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

Nếu bạn muốn biết liệu nó có thể gọi được với một tập hợp các hàm cụ thể hay không, bạn cần các hàm mà không có mặc định đã được chỉ định. Chúng có thể có được bằng cách:

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.

Sau đó, một chức năng để cho biết bạn đang thiếu những gì trong câu lệnh cụ thể của bạn là:

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

Tương tự, để kiểm tra các args không hợp lệ, hãy sử dụng:

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

Và vì vậy, một bài kiểm tra đầy đủ nếu nó có thể gọi được là:

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

(Điều này chỉ tốt khi phân tích cú pháp đối số của python. Bất kỳ thời gian chạy nào kiểm tra các giá trị không hợp lệ trong kwargs rõ ràng là không thể được phát hiện.)


Đẹp! Tôi không biết chức năng này!
DzinX 13/10/08

1
Cho rằng phương pháp sử dụng các đối tượng mã ít nhiều giống hệt nhau, liệu có lợi ích khi phải nhập thêm một mô-đun ...?
jmetz

@jmets - chắc chắn - sử dụng mô-đun thư viện hầu như luôn tốt hơn là sử dụng mô-đun của riêng bạn. Ngoài ra, các thuộc tính trên đối tượng mã mang tính nội bộ hơn và đang được thay đổi (ví dụ: lưu ý rằng điều này được chuyển sang trong pyhon3). Việc sử dụng mô-đun làm giao diện trong tương lai sẽ bảo vệ bạn nhiều hơn trong trường hợp một số nội dung này thay đổi. Nó cũng sẽ thực hiện những thứ bạn có thể không nghĩ đến, chẳng hạn như ném một lỗi loại thích hợp lên các hàm mà bạn không thể kiểm tra (ví dụ: các hàm C).
Brian

13
inspect.getargspec(f)không được dùng nữa kể từ Python 3.0; phương pháp hiện đại là inspect.signature(f).
gerrit

FYI, nếu bạn muốn hỗ trợ Cython và Python, phương pháp này không hoạt động trên một hàm Cython'd. Mặt co_varnameskhác, tùy chọn này hoạt động ở cả hai.
partofthething

32

Thao tác này sẽ in ra tên của tất cả các đối số có thể chuyển, từ khóa và không phải từ khóa:

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

Điều này là do đầu tiên co_varnamesluôn là các tham số (tiếp theo là các biến cục bộ, như ytrong ví dụ trên).

Vì vậy, bây giờ bạn có thể có một chức năng:

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)

Sau đó bạn có thể sử dụng như thế này:

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

CHỈNH SỬA : Một bổ sung nhỏ: nếu bạn thực sự chỉ cần các đối số từ khóa của một hàm, bạn có thể sử dụng func_defaultsthuộc tính để trích xuất chúng:

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)

Bây giờ bạn có thể gọi hàm của mình với các args đã biết, nhưng các kwargs được trích xuất, ví dụ:

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

Điều này giả định rằng funckhông sử dụng *argshoặc **kwargsma thuật trong chữ ký của nó.


Điều gì xảy ra nếu tôi chỉ muốn in ra các đối số "từ khóa" "các khóa"?
Jia

7

Trong 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

Đối với giải pháp Python 3, bạn có thể sử dụng inspect.signaturevà lọc theo loại thông số bạn muốn biết.

Lấy một hàm mẫu với các tham số từ khóa vị trí hoặc từ khóa, chỉ từ khóa, var positional và var:

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

Bạn có thể tạo một đối tượng chữ ký cho nó:

from inspect import signature
sig =  signature(spam)

và sau đó lọc bằng cách hiểu danh sách để tìm ra chi tiết bạn cần:

>>> # 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']

và, tương tự, đối với các vị trí var bằng cách sử dụng p.VAR_POSITIONALvà từ khóa var với VAR_KEYWORD.

Ngoài ra, bạn có thể thêm một mệnh đề vào if để kiểm tra xem giá trị mặc định có tồn tại hay không bằng cách kiểm tra nếu p.defaultbằng p.empty.


3

Mở rộng câu trả lời của 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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.