Category Archives: Блоги

Легенда про самодокументований код

Якось був код:

        if self.get_package_ids() == [None]: # only free package

Він був дещо незрозумілим, тому прокоментованим. Але потім в когось з’явилась ідея додати

    def only_free_package(self):
        return self.get_package_ids() == [None]

І коментар в нашому рядочку став зайвим:

        if self.only_free_package():

Ось і все. Це коротка легенда з простим сюжетом. Її мораль – методи призначені не тільки для повторного використання. Їх також варто використовувати для ізоляції рівнів абстракції.

Макконел також розповідає що можна писати спочатку псевдокод, потім його поступово закоментовувати (якщо він хороший він може бути документацією), і під ним писати решту коду.

Але дуже часто я бачив псевдокод нижчого рівня ніж Python. Я читав погані псевдокоди?


Filed under: Кодерство Tagged: паттерни, розробка, Python

Про конкуренцію в програмістів

Вона дивна, так що зразу й не скажеш що конкуренція, але я спробую її пояснити. Спочатку розкажу про те яка вона в інших професіях.

Якось на зустрічі з приводу десятиріччя вікіпедії я спілкувався з російським вікіпедистом (який жив в Києві достатньо довго щоб мене розуміти). Він фінансист з System Capital Management, чи щось в тому роді, точно не пам’ятаю. Так от він сказав, що по своїй спеціальності в вікіпедію не пише, щоб не допомагати конкурентам. Пише про цікаві місця Києва, для розваги. Мені було дивно що в мене прямо протилежні бажання. З мого блогу видно, що я всіма силами намагаюсь допомогти конкурентам.

Чому так? Тому що в програмуванні відбувається конкуренція не персон, а спільнот. Яка викликана дефіцитом останніх. Програміст Python буде цінуватись на ринку праці більше, якщо він зможе робити свою роботу краще. Він зможе робити свою роботу краще тільки коли в Python буде більше батарейок, менше багів, більше документації, більше блогів, більше форумів, більше колективного інтелекту.

З іншого боку програмісту самому вигідно допомагати іншим, бо це збільшує досвід, дозволяє отримати конструктивну критику без якої вдосконалюватись важко, і найголовніше – збільшує вплив. Якщо я відкрито продемонструю всім можливість писати крутіше ніж Роман Хоменко, то після того як всі про це дізнаються, приїду в Харків, і заміню його на його роботі :) .

Хоча насправді коли таке відбувається… А ніколи, тому що всі програмісти різні, і кожен майстер у своєму проекті. Вони від відсутності конкуренції навіть придумали спортивне програмування, де всі програмісти пишуть один і той самий проект. Ізольовано. Для розваги. Проект зазвичай не має жодної практичної цінності. І кожен контест для суспільства – це втрата кількох сотень дуже дорогих (за собівартістю і ринково) людино-годин.

Яким чином ведення блогу покращує конкретно мою позицію на ринку праці? З двох сторін.

З одного боку мій блог читають менш досвідчені програмісти, і я економлю їм час. Я не боюсь їх конкуренції, бо поки вони читають те що я вже знаю, я вивчаю щось нове. Але існує ймовірність що вони розкажуть про мене своєму начальнику, або колегам.

З іншого боку мій блог читають люди більш досвідчені, які можуть підказати правильну дорогу далі. Їм це вигідно, бо я можу розповісти про них своїм колегам.

От якби був надлишок програмістів, думаю такої чудової роботи і співпраці не тільки в командах, а й між ними ми б не спостерігали.


Filed under: Кодерство, Психософія Tagged: розробка

Про конкуренцію в програмістів

Вона дивна, так що зразу й не скажеш що конкуренція, але я спробую її пояснити. Спочатку розкажу про те яка вона в інших професіях.

Якось на зустрічі з приводу десятиріччя вікіпедії я спілкувався з російським вікіпедистом (який жив в Києві достатньо довго щоб мене розуміти). Він фінансист з System Capital Management, чи щось в тому роді, точно не пам’ятаю. Так от він сказав, що по своїй спеціальності в вікіпедію не пише, щоб не допомагати конкурентам. Пише про цікаві місця Києва, для розваги. Мені було дивно що в мене прямо протилежні бажання. З мого блогу видно, що я всіма силами намагаюсь допомогти конкурентам.

Чому так? Тому що в програмуванні відбувається конкуренція не персон, а спільнот. Яка викликана дефіцитом останніх. Програміст Python буде цінуватись на ринку праці більше, якщо він зможе робити свою роботу краще. Він зможе робити свою роботу краще тільки коли в Python буде більше батарейок, менше багів, більше документації, більше блогів, більше форумів, більше колективного інтелекту.

З іншого боку програмісту самому вигідно допомагати іншим, бо це збільшує досвід, дозволяє отримати конструктивну критику без якої вдосконалюватись важко, і найголовніше – збільшує вплив. Якщо я відкрито продемонструю всім можливість писати крутіше ніж Роман Хоменко, то після того як всі про це дізнаються, приїду в Харків, і заміню його на його роботі :) .

Хоча насправді таке відбувається ніколи, тому що всі програмісти різні, і кожен майстер у своєму проекті. Вони від відсутності конкуренції навіть придумали спортивне програмування, де всі програмісти пишуть один і той самий проект. Ізольовано. Для розваги. Проект зазвичай не має жодної практичної цінності. І кожен контест для суспільства – це втрата кількох сотень дуже дорогих (за собівартістю і ринково) людино-годин.

Яким чином ведення блогу покращує конкретно мою позицію на ринку праці? З двох сторін.

З одного боку мій блог читають менш досвідчені програмісти, і я економлю їм час. Я не боюсь їх конкуренції, бо поки вони читають те що я вже знаю, я вивчаю щось нове. Але існує ймовірність що вони розкажуть про мене своєму начальнику, або колегам.

З іншого боку мій блог читають люди більш досвідчені, які можуть підказати правильну дорогу далі. Їм це вигідно, бо я можу розповісти про них своїм колегам.

От якби був надлишок програмістів, думаю такої чудової роботи і співпраці не тільки в командах, а й між ними ми б не спостерігали.


Filed under: Кодерство, Психософія Tagged: розробка

Ознака зменшення складності

Їду в неділю в маршрутці. Читаю Макконела. Раптом розумію що хочу дещо записати, і виявляю що забув блокнот. Довелось писати на чеку, який я тримав як закладку. Виявилось все одно краще ніж в твіттері. В твіттері фіг напишеш щось таке абстрактне про програмування…

Отож, по Макконелу, основна проблема програмної інженерії – боротьба зі складністю. Складність в системі з’являється через зв’язки. Тому що зв’язки заважають відокремленому аналізу елементів. Тому, чим менше в системі зв’язків, тим вона простіша. Це очевидно.

Тепер, якщо ми замінимо слово “зв’язки” словом “залежності”, відношення про яке ми говоримо перестане виглядати симетричним. А воно і не є. Якщо A залежить від B, це ще не значить що B залежить від A.

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

І от, чим більше речей залежать від певного об’єкта системи – тим краще. Тому що розуміння одного цього об’єкта дасть нам ключі до розуміння багатьох. Чим від більшої кількості інших об’єктів залежить цей – тим гірше. Бо щоб зрозуміти його, доведеться розуміти багато всього іншого.

І взагалі, коли я бачу diff, в якому в 3 різних місцях кількість рядочків зменшується навіть на один, хоча для цього довелось додати ще три рядочки, написавши функцію, на душі чомусь приємно. Зменшується дублювання. Відбувається щось на зразок нормалізації коду.

А нормалізовані системи простіші за денормалізовані, бо зручно коли все в одному місці.

Ну, от так. Непогано, як для нотатки на чеку, правда?

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

Увага! Під словом “модуль” тут мають на увазі частини системи на певному рівні абстракції зв’язки між якими аналізують. Звичайно на певному рівні абстракції це можуть бути модулі в класичному розумінні – окремі файли програми. Але більш точним відповідником того що я маю на увазі тут під словом модуль буде функція.

Так от, виникає потреба в інструменті який би проводив аналіз коду, і виявляв компоненти зв’язності, виявляючи код що можна видалити. Pylint поки що для цього недостатньо підходить, бо виявляє проблеми дуже локально. І динамічна природа Пітона, та метапрограмування трохи цьому заважає. Також заважають тести. Бо кожен тест має свою точку входу, тому їх не треба враховувати. Але не забувати видаляти тести модулів з непотрібних компонент зв’язності.

Можливо треба буде якось написати такий інструмент. Хіба ж не для цього створили модуль _ast? ;)

А на наступній сторінці вас чекає велика і жахлива формула.


Filed under: Кодерство Tagged: розробка

Інтроспектор: “Хто присвоїв атрибуту це значення?”

Ану признавайтесь падлюки! :)

Іноді в процесі зневадження з’являється питання “Хто й коли присвоює цьому атрибуту неправильне значення?”. Покрокове проходження через тисячі рядків коду нудно і занадто складно. Що робити?

Елементарно, Ватсон! В Python надзвичайна здатність до інтроспекції.

Візьмімо для прикладу таку програму:

from watch_attribute import watched_attribute
import random

def change_here(x):
    x.x = random.random()

class Foo(object):
    add_watched_attribute('x')

foo = Foo()
foo.x = 2

for i in range(2):
    foo.x = foo.x * 2
    change_here(foo)

y = foo.x

Програма притягнута за вуха, суто щоб потестувати watched_attribute – функцію недавно написана мною, яка змушує атрибут доповідати про всі дії які з ним роблять. Результат роботи програми виглядає наприклад так:

/home/bunyk/progs/test_watch_attribute.py:13 foo.x = 2
x set to 2
/home/bunyk/progs/test_watch_attribute.py:16     foo.x = foo.x * 2
x is 2
/home/bunyk/progs/test_watch_attribute.py:16     foo.x = foo.x * 2
x set to 4
/home/bunyk/progs/test_watch_attribute.py:7     x.x = random.random()
x set to 0.288351784619
/home/bunyk/progs/test_watch_attribute.py:16     foo.x = foo.x * 2
x is 0.288351784619
/home/bunyk/progs/test_watch_attribute.py:16     foo.x = foo.x * 2
x set to 0.576703569239
/home/bunyk/progs/test_watch_attribute.py:7     x.x = random.random()
x set to 0.300510362936
/home/bunyk/progs/test_watch_attribute.py:19 y = foo.x
x is 0.300510362936

З прикладу незрозуміло нащо це треба, тому я покажу як я використав це на роботі. Отак:

('uaprom/forms/wt_fields.py', 1583) 'sets data to' 90000L
('wtforms/form.py', 133) 'gets data' 90000L
('wtforms/form.py', 133) 'gets data' 90000L
('uaprom/forms/wt_fields.py', 1583) 'sets data to' 90000L
('uaprom/forms/wt_fields.py', 1596) 'sets data to' 501L
('uaprom/forms/wt_fields.py', 1634) 'gets data' 501L
('uaprom/forms/wt_fields.py', 1635) 'gets data' 501L
('uaprom/forms/wt_fields.py', 1635) 'sets data to' 9018.0
('uaprom/forms/wt_forms.py', 931) 'gets data' 9018.0
('uaprom/forms/wt_validators.py', 721) 'gets data' 9018.0
('uaprom/forms/wt_validators.py', 722) 'gets data' 9018.0
('uaprom/forms/wt_validators.py', 722) 'sets data to' 501.0 <-- от хто винен!
('wtforms/validators.py', 150) 'gets data' 501.0
('uaprom/forms/wt_fields.py', 1629) 'gets data' 501.0

Звичайно треба ламати руки тому хто змінює значення полів в валідаторах, але я живим не дамся! Приклад нам показує що коли атрибут читають і пишуть 14 разів в 5 різних файлах, то перечитування коду чи навіть трасування програми не допоможе знайти проблему так швидко як такий лог. Бо поки залізеш в один файл, розпечатаєш там значення, поки в інший…

Ну, а зараз час подарувати вам код модуля watched_attribute:

import sys
import inspect

def from_where_called():
    info = inspect.getframeinfo(sys._getframe(2))
    # інформація про фрейм на два вище за поточний
    # (функція яка викликала функцію що викликала оцю функцію)
    code = info.code_context[0] if info.code_context else ''
    return '%s:%s %s' % (info.filename, info.lineno, code)

def add_watched_attribute(name):  
    def attr_watch_get(self):
        value = getattr(self, '_' + name, 'unset')
        print from_where_called(), name, 'is', value
        return value

    def attr_watch_set(self, value):
        print from_where_called(), name, 'set to', value
        setattr(self, '_' + name, value)

    sys._getframe(1).f_locals[name] = property(attr_watch_get, attr_watch_set)

Від чого я в цьому коді прусь, так це від того, що getter і setter для property сховані в замиканні, і не засмічують простір імен класу. А тепер також від того, що функція виявляється має доступ на запис, до простору імен на рівень вище (останній рядок). Ну хіба це не прекрасно?


Filed under: Кодерство Tagged: зневадження, Python

Додзьо для регулярного ніндзя

Щойно вивчив чим відрізняється negative lookbehing від negative lookahead.

Шукав всі пітонівські файли які не є скомпільованими мако файлами що містять певний шаблон. В моєму випадку – всі місця в проекті, в яких рендериться вміст листів. Написав:

    :Ack -G "(?!\.mako)\.py$" "email/[_/\w\d]+\.mako"

Не знайшов. Бачите в чому помилка? Тут я використав lookahead, а треба було lookbehind. Lookbehind пишеться так:

    :Ack -G "(?<!\.mako)\.py$" "email/[_/\w\d]+\.mako"

Хто б подумав, що треба писати так. Але тепер я вже знаю :) . Ах, для тих хто не зрозумів що відбувається, ack – це типу grep.

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

[
    {
        'Назва': "...",
        'Текст уроку': "...",
        'Вправи': [
            {
                'Умова': "...",
                'Корпус тексту': "...",
                'Тип вправи': 1, # пошук/заміна
                'Підрядки що потрібно отримати з домогою виразу': ["...", ...],  # якщо пошук
                'Текст що потрібно отримати в результаті': "...", # якщо заміна
                'Бали': XP, 
            },
            ...
        ]
    },
    ...
]

І тебе пускають до наступного коли ти набрав необхідну кількість балів на попередньому.

Тепер розшукую regexp – гуру, jQuery – гуру, гуру верстки, і технічних письменників (чи просто літературних редакторів) які б це все допомогли реалізувати.


Filed under: Кодерство, Павутина, Розмітка Tagged: JavaScript, розробка, цілі

Найпростіший спосіб вимірювання найгіршої метрики

Або іншими словами – примітивний облік робочого часу в лінуксах.

Для цього є команда – last. Дозволяє подивитись в логах час останніх входів і виходів з системи. Якщо наприклад дати команду

last 7 # еквівалентно last tty7

Дізнаємось хто коли і скільки користувався сьомим терміналом:

bunyk    tty7         :0               Thu Feb  2 08:30 - down   (09:32)

Вдома попробував – не працює. А виявляється все тому, що є певна конфігурація ротації логів /etc/logrotate.conf, в якій написано – monthly.

Але команду я дізнався на початку місяця, коли ще жодних логів не було навіть на роботі. Якось неправильно ця ротація працює. Вдома логи чомусь все одно ведуться за один день, хоча я й написав що хочу місячну ротацію. Може не в ті логи написав.

А ще збацав коротенький скриптик, який бере дані з last, і рахує середній робочий час:

#!/usr/bin/python3
import re
import subprocess 

def hours(s):
    s = s.split(':')
    return float(s[0]) + float(s[1]) / 60

def to_str(x):
    h = int(x)
    m = 60 * (x - h)
    return '%s:%s' % (h, int(m))

output = subprocess.check_output(['last', '7']).decode('utf-8')

data = (re.findall(r'\((\d\d:\d\d)\)', x) for x in output.splitlines())
data = [hours(x[0]) for x in data if x]

print(output)
print('Average time: ', to_str(sum(data)/len(data)))

bunyk@bunyk-laptop:~/workspace/uaprom$ /home/bunyk/experimental/timestat.py 
bunyk    tty7         :0               Thu Mar 22 09:32   still logged in   
bunyk    tty7         :0               Wed Mar 21 10:37 - 19:51  (09:13)    
bunyk    tty7         :0               Tue Mar 20 11:48 - down   (08:16)    
bunyk    tty7         :0               Mon Mar 19 11:38 - down   (07:14)    
bunyk    tty7         :0               Sat Mar 17 09:45 - down   (08:36)    
bunyk    tty7         :0               Fri Mar 16 10:35 - 19:23  (08:48)    
bunyk    tty7         :0               Thu Mar 15 10:22 - down   (08:47)    
bunyk    tty7         :0               Tue Mar 13 10:02 - 19:44  (09:42)    
bunyk    tty7         :0               Mon Mar 12 12:08 - 20:34  (08:25)    
bunyk    tty7         :0               Wed Mar  7 10:29 - down   (07:42)    
bunyk    tty7         :0               Tue Mar  6 09:54 - down   (09:50)    
bunyk    tty7         :0               Mon Mar  5 11:15 - 19:32  (08:16)    
bunyk    tty7         :0               Sat Mar  3 11:21 - down   (07:03)    
bunyk    tty7         :0               Fri Mar  2 12:08 - down   (08:00)    

wtmp begins Thu Mar  1 18:46:07 2012

Average time:  8:27

Виходить що я трохи більше ніж пів-години на добу недопрацьовую. Правда шпигунський роман Тоні ДеМарко розповідає про те що до програмістів краще не застосовувати метрики, бо ті хитрі зарази знайдуть спосіб максимізувати метрику не покращуючи продукт. Якою гарною ця метрика не була б (хай навіть зміною відношення кількості вад до кількості функціоналу). А Тоні ДеМарко – виявляється в вікіпедії стоїть поруч з такими гігантами як Вард Каннінгам, Мартін Фаулер і навіть Тоні Хоар. Напевне варто познайомитись детальніше. Правда часу нема. Але якщо він не важливий, тоді важливе лише те що я вважаю важливою дією в кожен момент часу.


Filed under: Інструменти, Кодерство Tagged: linux, робота, Python

Кібернетична вікі. Епізод II. Атака ботів

Не так давно
на одному
сервері кібцентру…

В Кібернетичній вікі
неспокійно. Більше сотні
користувачів демонструють
наміри закидати її спамом.

Рух спамерів очолюваний
загадковим невідомо ким
(чи взагалі ніким не очолюваний),
перешкоджає малочисельним кіберджедаям
зберігати чистоту й порядок на сайті.

Адміністрато bunyk повертається в КібВікі
щоб зайнятись принциповим питанням: створенням
бота для допомоги джедаям в малоуспішній боротьбі…

Пафосні титри подивились – переходим до банального сюжету про те як добро перемогло зло. :) Зі мною була нова сила (Python 3), і розуміння того що об’єктно-орієнтоване програмування призначене для зменшення кількості подібних параметрів в різних методах. І знання PEP8 яке дозволяє писати код на 8 та більше балів за Пайлінтом.

До того дії всіх ботів я відкидав вручну, слідкуючи за вікі через RSS. Коли їх було 1-2 на день це ще можна було терпіти. Але коли вони повалили десятками я на деякий час забив.

Я вирішив не морочити собі голову з PywikipediaБотом, для роботи якого на cybwiki ще треба створювати окремий файл конфігурації, а змайструвати свого. Свого писати набагато приємніше ніж лазити по сотнях файлів чужого. Крім того, мені дуже соромно за старий bunykwikibot, бо той код – блювотний. Зрозуміло що рік тому я цього не міг бачити. Тому маючи ще один день відпустки який залишився з проходження практики вирішив використати цей шанс щоб виправити ситуацію.

Знайомтесь – bunykwikibot2. Поки що різноманітних запитів до вікі в нього менше. Але зате він вміє логінитись і писати. І з ним приємно працювати.

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

#!/usr/bin/python3

Ага. Рухаємось вперед. Бо якщо не ми, то хто?

from api import Wiki

Простіше простого. І не треба завантажувати штук п’ять функцій!

def hunt_bots():
    wiki = Wiki('http://cybportal.univ.kiev.ua/w/api.php')
    wiki.session_file('bunyk_on_cybwiki.session')

Це в нашого скрипта головна функція. При запуску створює новий клієнт wiki, для API за заданою адресою. Другий рядочок каже що дані сесії (куки і ще дещо) будуть завантажуватись з вказаного файлу. А якщо їх там нема – попросити в користувача залогінитись і записати туди дані сесії.

    def marked_bot(user):
        userpage = user.userpage().read() or ''
        return '{{Spambot}}' in userpage

Як розрізнити спам і не спам? Пол Грем написав статтю про Баєсівський класифікатор, а я як останній неуч її все ще не читав. Тому вирішив просто позначити всіх ботів вручну, шаблончиком {{Spambot}}. Функція вище отримує текст сторінки користувача, і дивиться чи там не написано що користувач – бот.

Зрозуміло бота ще треба позначити. Цим займається наступна функція:

    def shoot_bot(bot):
        bot.userpage().write('{{Spambot}}', 'Попався!')

Перший її рядок замінює текст сторінки бота позначкою про те що він бот, супроводжуючи цю дію бойовим криком “Попався!”.

        for contrib in bot.contributions():
            spam_page = wiki.page(contrib['title'])
            only_bots = True
            for user in spam_page.contributors():
                if not marked_bot(user):
                    only_bots = False
                    break
            if only_bots:
                spam_page.delete('Спам.')

Далі по черзі розглядаються всі сторінки які бот редагував. І якщо єдиними редакторами цих сторінок були вже позначені боти – сторінки теж видаляються.

        bot.block('Die spammers, die!')

Після цього бот блокується з іншим грізним криком.

Але основна частина скрипта – визначення хто бот, а хто ні:

    for user in wiki.all_users:
        userpage = user.userpage()
        page_text = userpage.read()
        if page_text:
            if '{{Spambot}}' in page_text:
                print(user.name, '- ботяра')
                continue
            if '{{Approved}}' in page_text:
                print(user.name, '- чесний')
                continue
            print(page_text)
        else:
            print('Сторінка користувача порожня')
        edits = user.edit_count()
        print(userpage.title, 'Редагувань: %d' % edits)

Я розглядав кожного користувача. Якщо він вже позначений – виводив повідомлення і брав наступного. Якщо ж ні – виводив текст його сторінки користувача, логін і кількість редагувань.

        if edits < 3:
            verdict = True
        else:
            verdict = ask_y_n('Це бот, як думаєте?'
                '(y/n або порожній рядок якщо не знаєте)'
            )

Спочатку кожну справу я розглядав вручну. Потім до мене якось дійшло що кожен бот залогінювався і робив не більше двох редагувань. Видно вони на більше не розраховані бо їх банять і доводиться реєструвати нові аккаунти. Правда через це мало не “постраждав” Номіровський тому що статтю про себе він редагував переважно анонімно, але я все виправив руками.

        if verdict == '':
            continue
        elif verdict:
            shoot_bot(user)
        else:
            userpage.append(
                '{{Approved}}',
                'Підтвердження того що власник сторінки - не бот.'
            )

Ну і коли сторону користувача визначено, він або розстрілювався, або позначався як наш. Якщо не визначено – пропускався.

Ну після довгої підготовки бій був коротким:
Робота бота

Результати: заблоковано 114 ботів. 40 нормальних користувачів підтверджено.

Тепер напевне пора думати що робити з наступними навалами? Банити всіх хто не поставить на свою сторінку відповідний шаблон, а анонімів за географічною ознакою? Чи таки вчити баєсівський класифікатор розрізняти дифи? Побачимо.


Filed under: Кодерство, Павутина Tagged: CybWiki, вікіпедія, Python

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

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

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

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

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


Filed under: Кодерство Tagged: помилки, робота, Python

Визначення власності

На днях у Пола Грема вийшло ще одне вельми цікаве есе на тему приватної власності та копірайтів (в контексті нещодавних спроб прийняти SOPA та PIPA). По-моєму, дуже влучна стаття і, благо, невелика – вирішив перекласти. Читаючи її я також часто згадував про оцей комікс (англ. оригінал): “Я пробував подивитись Гру Тронів і ось що вийшло” і дибілізм до якого інколи доходять всілякі правові обмеження на поширення медіа в інтернеті: у мене є підписка на Crunchyroll та думаю якось таки замутити Spotify Unlimited для онлайн музла. Так от якого дідька я мушу користуватись цим всим через американські та британські проксі відповідно, якщо я навіть гроші вже плачу. По відео це взагалі алес… Я вже не кажу, що так само не можна офіційно з України користуватись онлайн сервісами типу Hulu, BBC iPlayer, тощо.

Автор оригіналу: Пол Грем (Paul Graham)
Оригінал: Defining Property

Elephant Chained

Photo by Invisible Lens Photography/Flickr

В дитинстві я читав книжку з історіями про відомого японського суддю XVIII сторіччя, якого звали Оока Тадаске (Ooka Tadasuke). Одна зі справ, яку він судив була порушена власником ресторану. Бідний студент, що міг дозволити собі лише рис, їв його насолоджуючись запахом інших смачностей, які доносились із ресторану. І власник хотів заставити студента заплатити за запахи, які він із задоволенням нюхав. Студент же крав запах їжі!

Я часто згадую цю історію, коли чую як RIAA та MPAA звинувачують людей у крадіжці музики чи кінострічок.

Трактувати запахи як чиюсь власність звучить для нас просто смішно. Але я можу придумати ситуацію, коли можна було б брати гроші і за запах. Просто уявіть, якби ми жили на місячній базі і купували повітря по літрам. Тоді можна з легкістю уявити і підмішування запаху до цього повітря в якості небезкоштовного бонусу.

Причина, того, що право власності на запах виглядає для нас повною маячнею в тому, що на Землі на це ніхто б не повівся. Але ж цілком можливо це спрацювало б на місячній базі.

Те що вважається власністю залежить від того, що можна взагалі сприймати за власність. І останнє може не лише змінюватись з часом, а вже змінилось. Люди завжди (для деяких визначень слів “люди” та “завжди”) сприймали за власність те, що вони носили з собою. Але прадавні люди, що займались збиральництвом, наприклад, ніколи не сприймали землю як власність в тому сенсі як ми зараз. [1]

Причина того, що так багато людей і досі думають, що у власності є лише одне незмінне визначення полягає в тому, що це визначення змінюється дуже повільно. [2] Але воно зараз якраз перебуває в стані трансформації. Рекордингові лейбли та кінематографічні студії звикли продавати своє добро ніби це повітря, що йде по трубах на місячній базі. Але з появою комп’ютерних мереж ми ніби переселилися на планету з атмосферою, якою можна дихати. Дані передаються зараз як запах. Але через прийняття бажаного за дійсне та короткострокової жадібності, лейбли та студії поставили себе на місце власника ресторану, звинуваючуючи користувачів в тому, що вони крадуть їх запахи.

(Причина через яку я кажу про короткострокову жадібність полягають в тому, що люди, які керують лейблами та студіями більше розраховують на бонуси, аніж на капітал. Якби вони працювали на капітал, то шукали б шляхи як використати технології замість того, аби протистояти їм. Та створювати нове – це дуже довго. Їхні бонуси залежать від цьогорічного прибутку, а найкаращий спосіб збільшити цей прибуток – це вичавити більше грошей з того, що вони вже мають.)

То що це значить? Невже люди не мають права брати гроші за свій контент? На це питання немає єдиної відповіді. Люди мусять мати змогу брати гроші за контент, коли це працює.

Під “працює” я маю на увазі щось більш суттєве, аніж “коли у них просто є така можливість”. Я маю на увазі, що люди можуть брати за нього гроші тоді, коли їм не треба заставляти суспільство робити це. Тобто компанії, що продають запахи на місячній базі можуть, звісно, продовжувати продавати їх на Землі, якщо вони успішно пролобіюють закони, які заставлять нас дихати через трубки і тут (навіть якщо нам це вже не потрібно).

Божевільні заходи по захисту прав, які зараз проводять лейбли та студії, дуже нагадують подібну ситуацію. Газетам та журналам теж непереливки, але вони принаймні занепадають з гідністю. RIAA та MPAA заставили б нас дихати через трубки, якби змогли.

Зрештою, все зводиться до здорового глузду. Коли ви зловживаєте правоохоронною системою, аби масово засудити випадкових людей в якості показовго покарання, чи лобіюєте закони, що спотворили б Інтерент, аби пройшли, то це по факту доказ того, що ви використовуєте визначення власності, яке вже не працює.

От тут дуже корисно мати працюючу демократію та багато незалежних країн. Якби у світі був єдиний авторитарний уряд, лейбли та студії купили б закони, які встановили те визначення власності, яке їм хочеться. Але на щастя є ще країни, які не є copyright-колоніями США і навіть в самих США політики все ще бояться електорату у великих кількостях. [3]

Людям, які керують у США може не подобатись, коли виборці чи інші країни відмовляються схилитись перед їхньою волею, але і ми зацікавлені в тому, аби люди, що намагають змінити закони заради власних інтересів, не мали чим нам дорікнути. Приватна власність – це дуже корисна ідея і, можливо, взагалі однин з найкращих винаходів людства. Досі кожне нове визначення приносило нам збільшення матеріального достатку [4]. Думаю, що не буде помилкою припустити, що і нове теж. Це було б трагедією, якби ми всі були змушені використовувати застарілу версію просто тому, що деякі впливові люди занадто ледачі аби оновити його.

Примітки

[1] Якщо вам цікаво дізнатись більше про збирачів, рекоменду. книжки Елізабет Машал Томас (Elizabeth Marshall Thomas): The Harmless People та The Old Way.

[2] Зміна визначення власності зазвичай спричиняється технологічним прогресом, щоправда оскільки технологічний прогрес прискорюється, цілком можливо, що і швидкість зміни визначення власності буде мінятися частіше. А це означає, що суспільству варто з граціозно пристосовуватися до цих змін, так як вони траплятимуться все частіше і частіше.

[3] Наскільки мені відомо термін “copyright-колонія” вперше використав Myles Peterson.

[4] Стан технології це не просто функція від визначення власності. Вони впливають одне на одне. І таким чином ви не можете з власної волі змінити визначення власності не зачепивши (зазвичай зашкодивши) розвиток технологій. Історія СРСР – яскравий тому приклад.