Блоґ одного кібера

Історія хвороби контуженого інформаційним вибухом

Небезпечні змінні об’єкти як значення аргументів функції за замовчуванням

with 3 comments

Якось пишу я один сайт (та все той же) і натикаюсь на отаку грізну помилку:

Через деякий час знайшлась і причина помилки, і те падло яке цю причину написало:

Bunyk Fri Oct 07 13:45 2011:   def __init__(self, label=u'', validators=[
Bunyk Fri Oct 07 18:31 2011:       validators.NumberRange(min=decimal.Decimal('0.01'),
Bunyk Fri Oct 07 13:45 2011:       message=_(u"Значение должно быть больше чем %(min)s"))
Bunyk Fri Oct 07 13:45 2011:       ], **kwargs
Bunyk Fri Oct 07 13:45 2011:   ):

Ну справді, хто так пише. Дужку варто закривати на тому ж рівні вкладеності на якому вона відкривалась це раз:

def __init__(self, label=u'',
    validators=[validators.NumberRange(
        min=decimal.Decimal('0.01'),
        message=_(u"Значение должно быть больше чем %(min)s")
    )],
    **kwargs
):

І два – списки, словники та інші змінні (mutable) об’єкти не надто бажані як значення аргументів за замовчуванням.

Чому так? Тому що це уможливлює ефекти про які я зараз розповім. А ці ефекти можуть здивувати, як і повідомлення на картинці.

Як відомо, в Python все є об’єктом. Навіть функції і класи. А про будь-який об’єкт найважливіше що варто знати – це коли і як він створюється.

Тепер давайте розглянемо який-небудь сферичний код в вакуумі. Наприклад такий:

def next(line=[]):
    line.append(len(line) + 1)
    print line
    
next()
# OUT: [1]
next()
# OUT: [1, 2]
next()
# OUT: [1, 2, 3]
next(range(10))
# OUT: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11]
next()
# OUT: [1, 2, 3, 4]
next(line=[])
# OUT: [1]
next()
# OUT: [1, 2, 3, 4, 5]

Так от, щоб зрозуміти чому він так поводиться, варто знати, що змінні (і аргументи функцій) в Python це насправді не значення а посилання. А [] – конструктор порожнього списку.

В першому рядку коду створюється об’єкт функції, разом з ним створюється об’єкт списку, і стандартним значенням параметра функції призначається посилання на цей список.

Можливий варіант переписування коду так щоб він працював більш “очікувано”:

def next(line=None):
    if not line:
        line = []
    line.append(len(line) + 1)
    print line

Ну а зараз строгостатичні чистолямбдасамці можуть срати цеглою нижче. 😉

Advertisements

Written by bunyk

Березень 22, 2012 at 21:53

Оприлюднено в Кодерство

Tagged with , ,

Відповідей: 3

Subscribe to comments with RSS.

  1. >>> Ну а зараз строгостатичні чистолямбдасамці можуть срати цеглою нижче.

    а тут таких немає ) код кул =)

    danbstt

    Березень 22, 2012 at 23:36

    • Дивно. Це ж величезне жахіття змінного стану. 🙂 Гірше тільки змінний стан в конкурентному середовищі.

      bunyk

      Березень 27, 2012 at 23:49

    • А от і є.

      Max Ulidtko

      Березень 28, 2012 at 15:19


Залишити відповідь

Заповніть поля нижче або авторизуйтесь клікнувши по іконці

Лого WordPress.com

Ви коментуєте, використовуючи свій обліковий запис WordPress.com. Log Out / Змінити )

Twitter picture

Ви коментуєте, використовуючи свій обліковий запис Twitter. Log Out / Змінити )

Facebook photo

Ви коментуєте, використовуючи свій обліковий запис Facebook. Log Out / Змінити )

Google+ photo

Ви коментуєте, використовуючи свій обліковий запис Google+. Log Out / Змінити )

З’єднання з %s

%d блогерам подобається це: