2017-06-14 59 views
3

help(getattr),兩個或三個參數被接受:是否可以編寫一個像getattr()那樣工作的函數簽名?

getattr(...) 
    getattr(object, name[, default]) -> value 

做一些簡單的測試,我們可以證實這一點:

>>> obj = {} 
>>> getattr(obj, 'get') 
<built-in method get of dict object at 0x7f6d4beaf168> 
>>> getattr(obj, 'bad', 'with default') 
'with default' 

太少/太多的爭論也像預期的那樣:

>>> getattr() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: getattr expected at least 2 arguments, got 0 
>>> getattr(obj, 'get', 'with default', 'extra') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: getattr expected at most 3 arguments, got 4 

幫助文本中指定的參數名似乎不被接受爲關鍵字參數:

>>> getattr(object=obj, name='get') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: getattr() takes no keyword arguments 

inspect模塊是沒有幫助在這裏:

>>> import inspect 
>>> inspect.getargspec(getattr) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/usr/lib/python2.7/inspect.py", line 816, in getargspec 
    raise TypeError('{!r} is not a Python function'.format(func)) 
TypeError: <built-in function getattr> is not a Python function 

(messaging is a little different in python3, but the gist is the same) 

現在的問題是:是否有寫我自己的Python功能與​​行爲完全像getattr的簽名的簽名一個簡單的方法?也就是說,關鍵字參數是不允許的,並且最小數量/最大數量的參數被執行?我來最接近的是以下幾點:

def myfunc(*args): 
    len_args = len(args) 
    if len_args < 2: 
     raise TypeError('expected at least 2 arguments, got %d' % len_args) 
    elif len_args > 3: 
     raise TypeError('expected at most 3 arguments, got %d' % len_args) 
    ... 

但現在,而不是像objectname有意義的參數名稱,我們得到args[0]args[1]。這也是很多的樣板,感覺很不愉快。我知道,作爲內置的,getattr必須與典型的Python代碼有很大的不同,並且可能沒有辦法完美地模擬它的行爲方式。但這是我一段時間以來的好奇心。

+0

@Makoto:不重複;可選參數很容易,但是這個問題特別針對僅限位置的參數行爲。 – user2357112

回答

3

這些類型的函數簽名是用C編寫的函數特有的,使用C級PyArg_Parse*函數族和Argument Clinic預處理器。在Python中沒有內置的方式來編寫這種簽名。你可以得到的最接近的是你已經使用的*args

(順便說一句,有已經語法的情況下,挑選出他們決定來實現這個功能,在PEP 457描述,但現在,這種語法在文檔中只使用,並有輕微變種用於論證診所。)

+0

但它通常可以完成。 – martineau

+0

@martineau:據我所知,最接近你可以手動解析'* args',這是提問者已經提出的。 – user2357112

+0

是的,但他們這樣做的方式可能並不是最簡單的。對於他們的問題中的特定示例,在我看來,就像一個簡單的'def myfunc(obj,name,default = None):'就足夠了 - 儘管如果它們需要用某種唯一值替換「None」一個合法的參數值。這也很容易做到...... – martineau

3

此代碼蜱你的大部分要求:

def anonymise_args(fn): 
    @functools.wraps(fn) 
    def wrap(*args): 
     return fn(*args) 
    return wrap 


@anonymise_args 
def myfunc(obj, name, default=None): 
    print obj, name, default 
  • 關鍵字參數是不允許的參數

    x.myfunc(obj=1, name=2) 
    TypeError: wrap() got an unexpected keyword argument 'obj' 
    
  • 一個minumum /最大數量的強制

    x.myfunc(1,2,3,4) 
    TypeError: myfunc() takes at most 3 arguments (4 given) 
    
  • 有意義的參數名稱

  • 不是很多的樣板

+0

你在測試中漏掉了'functools.wraps'嗎?第一個錯誤信息看起來不正確。 – user2357112

+0

另外,在wrap函數中使用有意義的返回值時,您應該'wrap'中的'return'來正確傳遞返回值。 – user2357112

+0

1)我在我的測試中加入了'functools.wraps'。我不知道爲什麼錯誤信息包含'wrap()'。 2)是的,'wrap'應該包含'return'。 –

相關問題