Архив метки: Python

Понимаем UnboundLocalError

Опять же на programmingwats.tumblr.com наткнулся на небольшую особенность питона.

my_str_1 = «1: outside of func»
my_str_2 = «2: outside of func»
def func_1():
    my_str_1 = «1: inside the func»
    my_str_2 = «2: inside the func»
    def func_2():
        print(my_str_1)
        print(my_str_2)
        my_str_1 = «1: inside the class»
    func_2()

func_1()
prints:
Traceback (most recent call last):
  File “”, line 1, in
  File “”, line 8, in func_1
  File “”, line 5, in func_2
UnboundLocalError: local variable ‘my_str_1’ referenced before assignment

В принципе на эту тему написано довольно много постов и погуглив можно спокойно найти объяснение. Но т.к. в свое время эта особенность питона попила у меня много кровушки я решил написать небольшой пост.

В чем причина такого поведения?
Причина заключается в том что питон преобразует переменную my_str_1 в локальную для func_2 и во время вызова функции print получает на вход не инициализированную переменную.
Решения проблемы тривиальные — либо убрать print, либо изменить название переменной, или объявить my_str_1 как глобальную.

Кто хочет более подробно ознакомиться с причинами такого поведения см ссылки, особенно с [2]

Ссылки

[1]https://docs.python.org/2/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value
[2]http://eli.thegreenplace.net/2011/05/15/understanding-unboundlocalerror-in-python/
[3]http://programmingwats.tumblr.com/page/2

Автор: Евгений Курочкин

python 2 утечка переменных цикла

Проглядывая programmingwats.tumblr.com нашел небольшую особенность python 2.

Посмотрим на пример:

>>>x = «top»
>>>print(list(«a» for x in (1,2)), x)
>>>print([«a» for x in (1,2)], x)

([‘a’, ‘a’], ‘top’)
([‘a’, ‘a’], 2)

Это бага питона, связанная с тем что локальная переменная x, используемая при создании списка становилась доступной в родительском пространстве имен. Думаю понятно чем грозит данная бага.
Данная бага поправлена в python 3.

Ссылки

https://docs.python.org/3/whatsnew/3.0.html#changed-syntax
http://programmingwats.tumblr.com/page/2
http://nbviewer.ipython.org/github/rasbt/python_reference/blob/master/tutorials/key_differences_between_python_2_and_3.ipynb?create=1

Автор: Евгений Курочкин

изменяемый объект в аргументах

Тема замусолена до невозможности, но не удержался увидев код Request

def func(f, l=[]):
    l.append(f)
    return l

func является потенциально опасной. Смотрим что происходит при её использовании

>>> print(func(1))
[1]
>>> print(func(2))
[1, 2]


l ссылается на один и тот же объект, который все время и изменяется (вспоминаем концепцию python)

Решение очевидное — определить список в функции

Такое решение используется и в популярном фреймворке request

class Request(RequestHooksMixin):
    def __init__(self,
        method=None,
        url=None,
        headers=None,
        files=None,
        data=None,
        params=None,
        auth=None,
        cookies=None,
        hooks=None):

        # Default empty dicts for dict params.
        data = [] if data is None else data
        files = [] if files is None else files
        headers = {} if headers is None else headers
        params = {} if params is None else params
        hooks = {} if hooks is None else hooks

Ссылки

https://github.com/kennethreitz/requests/blob/master/requests/models.py

Автор: Евгений Курочкин

приватные атрибуты в python

Ну нет приватных членов в питоне, ну нет…
Не надо писать С++/Java подобный код:

class C(object):
    def __init__(self):
        self.__name = ‘default’

def set_name(self, name):

        self.__name = name

def get_name(self):

        return self.__name

Пробуем
Читать

Опубликованно в разделе Python прикрепленные теги Дата публикации автор: .

ограничение lambda

Ограничение на использование лямбда-выражений

lambda x, y: assert x==y

К сожеления это не будет работать, т.к. в лямбде нельзя использовать встроенные выражения

Можно режить конкретную проблему

def assert_eq(x, y): 
    assert x == y

f = lambda x, y: assert_eq(x, y)


Но особого смысла в этом нет, т.к. мы лямбдой просто вызываем новую функцию assert_eq


Ссылки



Автор: Евгений Курочкин

aiopg и SQLAlchemy

Выпустил новую версию aiopg 0.2 — библиотеки для работы с PostgreSQL из asyncio.

aiopg использует асинхронные вызовы и в этом похож на txpostgres и momoko — библиотеки для работы с PostgreSQL под twisted и tornado соответственно.

В новой версии aiopg появилась опциональная поддержка SQLAlchemy Core Expressions.

Проще один раз показать.

Создаем описание структуры базы данных:

import sqlalchemy as sa

metadata = sa.MetaData()

users = sa.Table('users', metadata,
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('name', sa.String(255)),
sa.Column('birthday', sa.DateTime))

emails = sa.Table('emails', metadata,
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('user_id', None, sa.ForeignKey('users.id')),
sa.Column('email', sa.String(255), nullable=False),
sa.Column('private', sa.Boolean, nullable=False))

Как видите — две таблицы, связанные отношением один-ко-многим. Для тех, кто не знаком — алхимия позволяет описать любую модель данных, которая только может прийти в голову. Индексы, constraints, пользовательские типы данных такие как array и hstore — тоже.

Теперь нужно сделать engine:

from aiopg.sa import create_engine

engine = yield from create_engine(user='aiopg',
database='aiopg',
host='127.0.0.1',
password='passwd')

engine содержит внутри connection pool.

Для работы с БД нужно получить connection и что-нибудь выполнить:

with (yield from engine) as conn:
uid = yield from conn.scalar(
users.insert().values(name='Andrew', birthday=datetime(1978, 12, 9)))

Обратите внимание: диалект знает о INSERT ... RETURNING и позвращает primary key для вставляемой записи.

Работа с транзакциями:

with (yield from engine) as conn:
tr = yield from conn.begin()

# Do something

yield from tr.commit()

Получение данных:

with (yield from engine) as conn:
res = yield from conn.execute(users.select())
for row in res:
print(res)

Сложный запрос:

with (yield from engine) as conn:
join = sa.join(emails, users, users.c.id == emails.c.user_id)
query = (sa.select([users.c.name])
.select_from(join)
.where(emails.c.private == 0)
.group_by(users.c.name)
.having(sa.func.count(emails.c.private) > 0))

print("Users with public emails:")
ret = yield from conn.execute(query)
for row in ret:
print(row.name)

Вызов SQL функций:

with (yield from engine) as conn:
query = (sa.select([sa.func.avg(sa.func.age(users.c.birthday))])
.select_from(users))
ave = (yield from conn.scalar(query))
print("Average age of population is", ave,
"or ~", int(ave.days / 365), "years")

sa.func.avg и sa.func.age выполняются на стороне SQL сервера.

Полный код примера здесь, документация здесь.

Автор: Andrew Svetlov