开发者

Explicit argument in signature does not work? Very odd

开发者 https://www.devze.com 2023-03-28 10:14 出处:网络
I\'m trying to understand why explicitly specifying the signature arguments doesn\'t work, but just blindly doing an *args, **kwargs works! I really don\'t see much difference between the two?

I'm trying to understand why explicitly specifying the signature arguments doesn't work, but just blindly doing an *args, **kwargs works! I really don't see much difference between the two?

Example that does not work:

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!

and I get the following error:

  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'

While this works just fine:

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) 

In django source, django.db.models.subclassing.call_with_connection_and_prepared.inner(),I see there is some deletion of kwargs, but not entirely sure why?


The thing is, the connection argument is supposed to be always passed in as a keyword argument. The code in django.db.models.fields.subclassing only checks whether it is present in the kwargs dictionary, if not, it issues a DeprecationWarning and adds it in there. The positional arguments are not checked, so what happens in the end is that both the positional argument you passed gets forwarded, but the keyword argument provided by default by the function wrapper gets passed in as well. Hence the conflict.

To make your code work, all you need to do is this:

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

Just FYI, in the development version all those wrappers have been removed, which means your current code would probably work against trunk. However, it is considered best to keep the arguments in kwargs.


I don't have the django source available, so this is just a guess:

Notice that what gets passed to Foo is different depending on the call signature in Bar and Baz:

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

When you use

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

prepared gets passed into the positional argument list args.

But when you use

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

prepared gets passed into the keyword dict kwargs.


For functions defined using the "double splat" syntax, as I call it, the caller must explicitly give the dictionary key for each keyword arguments. I would bet that the get_db_prep_value is defined like the first of the below functions.

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
0

精彩评论

暂无评论...
验证码 换一张
取 消