簡體   English   中英

如何有效地傳遞參數(** python中的kwargs)

[英]how to pass arguments efficiently (**kwargs in python)

我有一個繼承自其他2個類的類。 這些是基類:

class FirstBase(object):
      def __init__(self, detail_text=desc, backed_object=backed_object,
                   window=window, droppable_zone_obj=droppable_zone_obj,
                   bound_zone_obj=bound_zone_object,
                   on_drag_opacity=on_drag_opacity):
          # bla bla bla

class SecondBase(object):
      def __init__(self, size, texture, desc, backed_object, window):
          # bla bla bla

這就是孩子:

class Child(FirstBase, SecondBase):
       """ this contructor doesnt work
       def __init__(self, **kwargs):
          # PROBLEM HERE
          #super(Child, self).__init__(**kwargs)
       """
       #have to do it this TERRIBLE WAY
       def __init__(self, size=(0,0), texture=None, desc="", backed_object=None,
                    window=None, droppable_zone_obj=[], bound_zone_object=[],
                    on_drag_opacity=1.0):
          FirstBase.__init__(self, detail_text=desc, backed_object=backed_object,
                             window=window, droppable_zone_obj=droppable_zone_obj,
                             bound_zone_obj=bound_zone_object,
                             on_drag_opacity=on_drag_opacity)
          SecondBase.__init__(self, size, texture, desc, backed_object, window)

我想用**kwargs很好地解決它,但當我調用第一個注釋掉的構造函數時,我得到TypeError: __init__() got an unexpected keyword argument 'size'

我有什么想法可以讓它與**kwargs?

您的問題是您只嘗試在子類中使用super

如果你也在基類中使用super ,那么這將有效。 每個構造函數將“吃掉”它所采用的關鍵字參數,而不是將它們傳遞給下一個構造函數。 當調用object的構造函數時,如果剩下任何關鍵字參數,則會引發異常。

class FirstBase(object):
    def __init__(self, detail_text=None, backed_object=None,
                 window=None, droppable_zone_obj=None,
                 bound_zone_obj=None, on_drag_opacity=None, **kwargs):
        super(FirstBase, self).__init__(**kwargs)

class SecondBase(object):
    def __init__(self, size=(0,0), texture=None, desc="",
                 backed_object=None, window=None, **kwargs):
        super(SecondBase, self).__init__(**kwargs)

class Child(FirstBase, SecondBase):
    def __init__(self, **kwargs):
        super(Child, self).__init__(**kwargs)

如您所見,它可以工作,除非您傳遞一個偽造的關鍵字參數:

>>> Child()
<__main__.Child object at 0x7f4aef413bd0>
>>> Child(detail_text="abc")
<__main__.Child object at 0x7f4aef413cd0>
>>> Child(bogus_kw=123)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "test.py", line 14, in __init__
    super(Child, self).__init__(**kwargs)
  File "test.py", line 5, in __init__
    super(FirstBase, self).__init__(**kwargs)
  File "test.py", line 10, in __init__
    super(SecondBase, self).__init__(**kwargs)
TypeError: object.__init__() takes no parameters

我不喜歡這個解決方案,但你可以這樣做:

class FirstBase(object):
  def __init__(self, *args, **kargs):
      kargs.get('karg name', 'default value')
      # bla bla bla

class SecondBase(object):
  def __init__(self, *args, **kargs):
      kargs.get('karg name', 'default value')
      # bla bla bla

class Child(FirstBase, SecondBase):
   def __init__(self, *args, **kargs):
      FirstBase.__init__(self, *args, **kargs)
      SecondBase.__init__(self, *args, **kargs)

因為FirstBase.__init__沒有size參數。 您可以添加size參數或將**kwargs添加到FirstBase.__init__

嘗試這樣的事情。

class FirstBase(object):
      def __init__(self, detail_text, backed_object, window, droppable_zone_obj, bound_zone_obj, on_drag_opacity):
          # bla bla bla

class SecondBase(object):
      def __init__(self, texture, desc, backed_object, window, size=None):
          # bla bla bla

一個可能的黑客(但黑客是壞的,記住),是要求檢查模塊獲取你將要使用的功能的簽名:

import inspect
#define a test function with two parameters function
def foo(a,b):
    return a+b

#obtain the list of the named arguments
acceptable = inspect.getargspec(f).args

print acceptable
#['a', 'b']

#test dictionary of kwargs
kwargs=dict(a=3,b=4,c=5)

#keep only the arguments that are both in the signature and in the dictionary
new_kwargs = {k:kwargs[k] for k in acceptable if k in kwargs}

#launch with the sanitized dictionary
f(**new_kwargs)

這不是您應該在生產代碼中使用的代碼。 如果函數拒絕某些參數,則意味着您可以獲取讀取代碼的簽名,並僅傳遞必要的鍵。 檢查的黑客程度遠不那么穩定和可讀,所以只有在沒有其他任何方式時才使用!

由於你可以控制代碼,所以讓你自己以你想要的方式傳遞** kwargs的簡單方法是在兩個基類的__init__中添加一個** kwargs參數,如下所示:

class FirstBase(object):
  def __init__(self, detail_text=desc, backed_object=backed_object,
               window=window, droppable_zone_obj=droppable_zone_obj,
               bound_zone_obj=bound_zone_object,
               on_drag_opacity=on_drag_opacity, **kwargs):
      # bla bla bla

class SecondBase(object):
      def __init__(self, size, texture, desc, backed_object, window, **kwargs):
          # bla bla bla

您遇到的失敗是因為您的基類只有命名參數,因此當您傳遞一個未知名稱時,保鏢聲稱您不在我的列表中。 通過將** kwargs參數添加到基類的__init__函數中,它們突然允許任意命名值被簡單地傳入,這是可以理解的,因為畢竟** kwargs是一個開放式的catch all,其中沒有任何東西是命名,那怎么知道排除什么? 它只是在被調用代碼的眼睛中結束了一個命名值的字典,它使用或不使用它們。 你可以完全忽略** kwargs。

在寫作的那一刻,目前被Dietrich認為是正確的答案,這個答案混淆了基本問題 - 它與super()完全沒有關系,而且他的修正只是因為** kwargs被添加而且為任意名字打開了大門。

如果您無法控制代碼,則可以:

  • 顯式傳遞參數,以便不會解壓縮args太長時間,或者使用未指定名稱的** kwargs,
  • 使用字典pop函數彈出kwargs中的無關參數,
  • 或者您可以使用此輔助元函數:

     import inspect def anykwarg(function, *args, **kwargs): validargs = inspect.getargspec(function).args removals = [key for key in kwargs.keys() if key not in validargs] for removal in removals: del kwargs[removal] return function(*args, **kwargs) 

可以改進這個anykwarg幫助器以檢查kwargs參數,如果存在則不從傳入的kwargs中刪除任何值,並且上面使用的inspect.getargspec返回該信息。 注意,在anykwarg中可以從kwargs中刪除它,因為它不是對傳入的某些字典的引用,它是從參數生成的本地字典。

你不應該在基類中調用super() ,就像@Dietrich Epp接受的答案一樣 你所做的只是將任何剩余的參數傳遞給kwargsobject基類,然后扔掉它們; 這很草率。

當你做super(ChildClass, self).__init__(foo, bar, **kwargs)ChildClass的超類必須要求所有參數foobar**kwargs 因此,在您的情況下,獲取錯誤TypeError: __init__() got an unexpected keyword argument 'size'意味着您的父類Child沒有size或者__init__()方法中沒有**kwargs

當你的super()調用轉到FirstbaseSecondbase每一個時,它們的構造函數將嘗試將**kwargs解包為其關鍵字參數。 **kwargs所有東西都需要去某個地方。 剩下的任何東西(即沒有由關鍵字參數指定),如size ,都會引發你的TypeError,除非剩下剩下的**kwargs進入。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM