签名中的显式参数不起作用?很奇怪

时间:2011-08-15 17:02:58

标签: python django django-models

我试图理解为什么明确指定签名参数不起作用,但只是盲目地做一个* args,** kwargs有效!我真的看不出两者之间有多大区别?

不起作用的示例:

from django.db.models import CharField as _CharField

class CharField(_CharField):
    def get_db_prep_value(self, value, connection, prepared=False):
        if self.blank == self.null == self.unique == True and value == '':
            value = None

        return super(CharField, self).get_db_prep_value(value, connection, prepared) # <--- this does not work!

我收到以下错误:

  File "/home/googledroid/Workspace/eclipse/gameproject/virtualenv/lib/python2.6/site-packages/django/db/models/fields/__init__.py", line 276, in get_db_prep_save
    return self.get_db_prep_value(value, connection=connection, prepared=False)
  File "/home/googledroid/Workspace/eclipse/gameproject/virtualenv/lib/python2.6/site-packages/django/db/models/fields/subclassing.py", line 53, in inner
    return func(*args, **kwargs)
  File "/home/googledroid/Workspace/eclipse/gameproject/src/fields/__init__.py", line 13, in get_db_prep_value
    return super(CharField, self).get_db_prep_value(value, connection, prepared)
  File "/home/googledroid/Workspace/eclipse/gameproject/virtualenv/lib/python2.6/site-packages/django/db/models/fields/subclassing.py", line 53, in inner
    return func(*args, **kwargs)
  File "/home/googledroid/Workspace/eclipse/gameproject/virtualenv/lib/python2.6/site-packages/django/db/models/fields/subclassing.py", line 53, in inner
    return func(*args, **kwargs)
TypeError: get_db_prep_value() got multiple values for keyword argument 'connection'

虽然这很好用:

from django.db.models import CharField as _CharField

class CharField(_CharField):
    def get_db_prep_value(self, value, *args, **kwargs):
        if self.blank == self.null == self.unique == True and value == '':
            value = None

        return super(CharField, self).get_db_prep_value(value, *args, **kwargs) 

在django来源django.db.models.subclassing.call_with_connection_and_prepared.inner(),我看到有一些kwargs的删除,但不完全确定为什么?

3 个答案:

答案 0 :(得分:3)

问题是,connection参数应该始终作为关键字参数传递。 django.db.models.fields.subclassing中的代码仅检查它是否存在于kwargs字典中,如果不存在,则会发出DeprecationWarning并将其添加到其中。不检查位置参数,所以最后发生的是你传递的位置参数都被转发,但函数包装器默认提供的关键字参数也会被传入。因此,冲突。

要使代码正常工作,您需要做的就是:

        return super(CharField, self).get_db_prep_value(value, connection=connection, prepared=prepared)

仅供参考,在开发版本中,所有这些包装器都已被删除,这意味着您当前的代码可能会对主干进行操作。但是,最好将参数保留在kwargs

答案 1 :(得分:0)

我没有django来源,所以这只是猜测:

请注意,传递给Foo的内容会有所不同,具体取决于BarBaz中的来电签名:

class Foo(object):
    def get_db_prep_value(self,*args,**kwargs):
        print(args,kwargs)

class Bar(Foo):
    def get_db_prep_value(self,value,connection,prepared=False):
        super(Bar,self).get_db_prep_value(value,connection,prepared)

class Baz(Foo):
    def get_db_prep_value(self,*args,**kwargs):        
        super(Baz,self).get_db_prep_value(*args,**kwargs)

bar=Bar()
bar.get_db_prep_value(1,2,prepared=True)
# ((1, 2, True), {})

baz=Baz()
baz.get_db_prep_value(1,2,prepared=True)
# ((1, 2), {'prepared': True})

使用时

super(Bar,self).get_db_prep_value(value,connection,prepared)

prepared被传递到位置参数列表args

但是当你使用

super(Baz,self).get_db_prep_value(*args,**kwargs)

prepared会传递到关键字dict kwargs

答案 2 :(得分:0)

对于使用“double splat”语法定义的函数,正如我所说,调用者必须为每个关键字参数显式提供字典键。我敢打赌,get_db_prep_value的定义与下面第一个函数类似。

def usessplat(**kwargs):
  print 'connection = ' + kwargs.get('connection', 'None')

def nosplat(connection=None)
  print 'connection = ' + str(connection)

usessplat('foo')  # raises TypeError
nosplat('foo')  # no exception