Явный аргумент в подписи не работает?Очень странно - PullRequest
1 голос
/ 15 августа 2011

Я пытаюсь понять, почему явное указание аргументов подписи не работает, а просто слепо делает * 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 ]

3 голосов
/ 15 августа 2011

Дело в том, что аргумент connection должен всегда передаваться как аргумент ключевого слова. Код в django.db.models.fields.subclassing только проверяет, присутствует ли он в словаре kwargs, если нет, выдает DeprecationWarning и добавляет его туда. Позиционные аргументы не проверяются, так что в итоге происходит то, что оба переданных вами позиционных аргумента перенаправляются, но также передается ключевой аргумент, предоставленный по умолчанию оболочкой функции. Отсюда и конфликт.

Чтобы ваш код работал, все, что вам нужно сделать, это:

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

Только к сведению, в версии для разработки все эти обертки были удалены, что означает, что ваш текущий код, вероятно, будет работать против транка. Однако считается, что аргументы лучше хранить в kwargs.

0 голосов
/ 15 августа 2011

Для функций, определенных с использованием синтаксиса «двойных сплатов», как я его называю, вызывающая сторона должна явно указывать ключ словаря для каждого ключевого аргумента.Держу пари, что 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
0 голосов
/ 15 августа 2011

У меня нет доступного источника django, так что это всего лишь предположение:

Обратите внимание, что то, что передается на Foo, отличается в зависимости от подписи вызова в Bar и 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})

Когда вы используете

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

prepared передается в список позиционных аргументов args.

Но когда вы используете

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

prepared передается в ключевое слово dict kwargs.

...