2008-10-20 19 views
167

Với chức năng python:Bắt tên tham số phương pháp trong python

def aMethod(arg1, arg2): 
    pass 

Làm thế nào tôi có thể trích xuất số lượng và tên của các đối số. I E. cho rằng tôi có một tham chiếu đến func, tôi muốn các func. [cái gì] để trở về ("arg1", "arg2")

Kịch bản sử dụng cho điều này là tôi có một trang trí, và tôi muốn sử dụng đối số phương thức theo cùng thứ tự mà chúng xuất hiện cho hàm thực tế dưới dạng khóa. I E. sẽ như thế nào giao diện trang trí mà in "a, b" khi tôi gọi aMethod ("a", "b")

+1

Đối với một danh sách khác nhau của câu trả lời cho một câu hỏi gần như giống hệt, xem [bài này stackoverflow khác] (http://stackoverflow.com/câu hỏi/582056/get-list-of-parameters-bên trong-python-chức năng) –

+1

Tiêu đề của bạn là gây hiểu lầm: khi người ta nói 'phương pháp' wrt từ 'chức năng', người ta thường nghĩ về một phương pháp lớp học. Đối với chức năng, câu trả lời được lựa chọn của bạn (từ Jouni K. Seppanen) là tốt. Nhưng đối với phương thức (lớp), nó không hoạt động và giải pháp kiểm tra (từ Brian) nên được sử dụng. –

Trả lời

278

Hãy xem mô-đun inspect - điều này sẽ thực hiện việc kiểm tra các thuộc tính đối tượng mã khác nhau cho bạn.

>>> inspect.getargspec(aMethod) 
(['arg1', 'arg2'], None, None, None) 

Các kết quả khác là tên của biến * args và ** kwargs và giá trị mặc định được cung cấp. I E.

>>> def foo(a,b,c=4, *arglist, **keywords): pass 
>>> inspect.getargspec(foo) 
(['a', 'b', 'c'], 'arglist', 'keywords', (4,)) 

Lưu ý rằng một số cuộc gọi có thể không quan tâm đến một số triển khai nhất định của Python. Ví dụ, trong CPython, một số hàm dựng sẵn được định nghĩa trong C không cung cấp siêu dữ liệu về các đối số của chúng. Do đó, bạn sẽ nhận được ValueError trong trường hợp bạn sử dụng inspect.getargspec() với chức năng tích hợp sẵn.

Kể từ Python 3.3, bạn có thể sử dụng cũng là inspect.signature() để biết chữ ký cuộc gọi của một đối tượng có thể được gọi:

>>> inspect.signature(foo) 
<Signature (a, b, c=4, *arglist, **keywords)> 
+0

xem thêm http://stackoverflow.com/a/3999574/288875 về kiểm tra các cuộc gọi nói chung –

+17

Làm thế nào để mã có thể biết rằng tham số mặc định '(4,)' tương ứng với tham số từ khóa 'c' cụ thể? – fatuhoku

+47

@fatuhoku Tôi đã tự hỏi điều tương tự. Hóa ra nó không mơ hồ vì bạn chỉ có thể thêm đối số mặc định ở cuối trong một khối tiếp giáp. Từ các tài liệu: "nếu tuple này có n phần tử, chúng tương ứng với n phần tử cuối cùng được liệt kê trong args" – Soverman

86

Trong CPython, số lượng các đối số là

aMethod.func_code.co_argcount 

và tên của họ đang ở trong đầu của

aMethod.func_code.co_varnames 

Đây là các chi tiết triển khai của CPython, vì vậy điều này có thể không hoạt động trong các triển khai khác của Python, chẳng hạn như IronPython và Jython.

Một cách di động để thừa nhận đối số "chuyển tiếp" là xác định hàm của bạn bằng chữ ký func(*args, **kwargs). Điều này được sử dụng rất nhiều trong ví dụ: matplotlib, trong đó lớp API bên ngoài chuyển rất nhiều đối số từ khóa đến API cấp thấp hơn.

+0

co_varnames không hoạt động với Python chuẩn, nhưng phương pháp này không thích hợp hơn vì nó cũng sẽ hiển thị các đối số nội bộ. – MattK

+11

Tại sao không sử dụng aMethod.func_code.co_varnames [: aMethod.func_code.co_argcount]? – hochl

+12

'func_code' bây giờ có thể được tìm thấy dưới dạng' __code__' –

13

Đây là điều mà tôi nghĩ sẽ làm việc cho những gì bạn muốn, bằng cách sử dụng trang trí.

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") 

Chạy nó, nó sẽ mang lại kết quả như sau:

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. 
10

Tôi nghĩ rằng những gì bạn đang tìm kiếm là người dân địa phương phương pháp -


In [6]: def test(a, b):print locals() 
    ...: 

In [7]: test(1,2)    
{'a': 1, 'b': 2} 
+6

Điều này là vô ích bên ngoài một chức năng mà là bối cảnh quan tâm ở đây (trang trí). –

+3

Trên thực tế chính xác những gì tôi đang tìm kiếm mặc dù nó không phải là câu trả lời cho câu hỏi ở đây. – javabeangrinder

19

Trong một phương pháp trang trí, bạn có thể liệt kê các đối số của phương pháp gốc theo cách này:

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) 
.210

Nếu **kwargs rất quan trọng cho bạn, sau đó nó sẽ là một chút phức tạp:

 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() 

Ví dụ:

@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] 
3

Kể từ khi câu trả lời là một chút cũ và phải làm một số sửa đổi để làm cho nó làm việc cho Python3, tôi đang thêm phiên bản của mình:

def _get_args_dict(fn, args, kwargs): 
    args_names = fn.__code__.co_varnames[:fn.__code__.co_argcount] 
    return {**dict(zip(args_names, args)), **kwargs} 

Phương thức này trả về một từ điển chứa cả arg và kwarg.

-1

Còn khoảng dir()vars() bây giờ?

vẻ làm chính xác những gì đang được hỏi siêu đơn giản ...

(Từ bên trong hàm/phương pháp)

+0

dir() trả về danh sách tất cả các tên biến ['var1', 'var2'], vars() trả về từ điển dưới dạng {'var1': 0, 'var2': 'something'} từ trong phạm vi cục bộ hiện tại. Nếu ai đó muốn sử dụng các biến đối số tên sau này trong hàm, chúng sẽ lưu trong một biến cục bộ khác, bởi vì gọi nó sau này trong hàm mà chúng có thể khai báo một biến cục bộ khác sẽ "làm ô nhiễm" danh sách này. Trong trường hợp họ muốn sử dụng nó bên ngoài hàm, chúng phải chạy hàm ít nhất một lần và lưu nó trong biến toàn cầu. Vì vậy, tốt hơn là sử dụng mô-đun kiểm tra. –

1

Cập nhật cho câu trả lời của Brian. Nếu hàm trong Python 3 có từ khóa chỉ đối số, sau đó bạn cần phải sử dụng inspect.getfullargspec:

def yay(a, b=10, *, c=20, d=30): 
    pass 
inspect.getfullargspec(yay) 

mang này:

FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=(10,), kwonlyargs=['c', 'd'], kwonlydefaults={'c': 20, 'd': 30}, annotations={}) 
2

Trả về một danh sách các tên đối số, chăm sóc partials và chức năng thường xuyên :

def get_func_args(f): 
    if hasattr(f, 'args'): 
     return f.args 
    else: 
     return list(inspect.signature(f).parameters) 
4

Python 3.5+

D eprecationWarning: inspect.getargspec() bị phản đối, sử dụng inspect.signature() thay vì

Vì vậy, trước đó:

func_args = inspect.getargspec(function).args 

Bây giờ là:

func_args = list(inspect.signature(function).parameters.keys()) 

Để kiểm tra:

'arg' in list(inspect.signature(function).parameters.keys()) 

Cho rằng chúng ta có chức năng 'chức năng' mà lấy đối số 'arg', điều này sẽ đánh giá là True, nếu không là False.

Ví dụ từ python console:

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 
2

Trong python 3. + với đối tượng Signature trong tầm tay, một cách dễ dàng để có được một ánh xạ giữa tên args để giá trị, đang sử dụng phương pháp của Chữ ký bind()!

Ví dụ ở đây là một trang trí cho in một bản đồ như thế:

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 wil print: {'kwargs': {'extra': 'baz'}, 'param_with_default': 'bars', 'y': 2, 'x': 1}