събота, 17 януари 2009 г.

Нишки в Python

„ Програмиране с Python“, ФМИ
Стефан Кънев & Николай Бачийски
28.05.2007г.
Многозадачност

Класически проблем е да накараме една програма да върши няколко неща едновременно. Има два стандартни подхода в повечето езици - fork-ове и нишки. Както можете да очаквате, те се срещат в Python в модулите os и threading.
fork

fork е функция, чието изпълнение създава ново копие на програмата, което продължава от същото място където е извикана функцията. Досега заделените ресурси са общи за двата процеса, докато новите са локални. Създава се нов процес на ниво операционна система. В родителския процес функцията връща id-то на дъщерния, докато във дъщерния връща 0.


import os

print "pre-forking"
pid = os.fork()
print "post-forking", pid

pre-forking
post-forking 0
post-forking 6450

fork (2)

Отделни процеси могат да си комуникират по-успешно с други функции в модула os. Като цяло fork е твърде примитивен подход когато имате нужда от конкурентност в приложението и в почти всички случаи е много по-добре да използвате по-високо ниво на абстракция - нишки.
thread и threading

Python предлага два модула за работа с нишки - thread и threading. Втория предлага по-високо ниво на абстракция и по-удобен интерфейс. Съответно е по-логичния избор от двата модула, ако нямате смислена причина да предпочетете thread.
threading.Thread

Класът Thread ви дава прост механизъм да изпълнявате код в нова нишка. Нужно е да предоставите метод run, който да съдържа кода на нишката. След това я инстанцирате и викате методът и start.


import threading

class MyThread(threading.Thread):
def run(self):
for i in range(0, 10000): print i

thread = MyThread()
thread.start()
for i in range(0, 10000): print -i

theading.Lock

Стандартен проблем при конкуренти задачи са "критични секции" от програмата - такива, които трябва да бъдат изпълнявани само веднъж в даден момент. Това може да се постигне с класът Lock. Неговите инстанции имат два метода - acquire() и block(). Класът вътре пази флаг дали lock-а е свободен или зает. Ако acquire() се извика на свободен lock, флага се вдига и lock-а става зает. Ако acquire() се извика на зает lock, метода блокира докато lock-а не се освободи. Извикването на release() освобождава lock-а.
theading.Lock (2)


import threading

lock = threading.Lock()

class Gatherer(threading.Thread):
def run(self):
while True:
foundStuff = gather()
lock.acquire()
for thing in foundStuff: report(thing)
lock.release()


g1, g2 = Gatherer(), Gatherer()
g1.start()
g2.start()

threading.Lock (3)

Можете да използвате with с Lock.


from __future__ import with_statement
import threading

lock = threading.Lock()
with lock:
print "Do stuff"

threading.Semaphore

Semaphore представлява механизъм за разрешаване на конкурентен достъп, много подобен на Lock разликата е там, че вместо флаг, вътрешно се пази брояч. acquire() го намалява, докато release() го увеличава. acquire() се разблокира, когато брояча е по-голям от нула и блокира, ако той е нула.
threading.Event

Event е проста комуникация между нишки, в която няколко нишки изчакват една да завърши с някакъв резултат. Нишките които изчакват викат метода wait() и изпълнението им блокира. Нишката, която продуцира резултата извиква метода set(). В този момент всички нишки които чакат продължават изпълнението си.
threading.Condition

Condition е много сходен с Lock, но дава няколко нови метода. wait() освобождава lock-а, след което блокира докато не се събуди с извикване на notify() или notifyAll(). Като се "събуди", отново взема lock-а. notify() събужда една нишка, заспала с wait(). notifyAll() събуджа всички.


# Consume one item
cv.acquire()
while not an_item_is_available():
cv.wait()
get_an_available_item()
cv.release()

# Produce one item
cv.acquire()
make_an_item_available()
cv.notify()
cv.release()


threading.local

Инстанциите на local представляват обекти, които са "локални" за дадена нишка. Тяхните атрибути са уникални за всяка нишка. Например:


import threading
foo = threading.local()

foo.blah = 1

class MyThread(threading.Thread):
def run(self):
if (hasattr(foo, 'blah')): print "MyThread sees foo.blah"
foo.blah = 4
print "MyThread(foo.blah) = ", foo.blah

MyThread().start()
print "BaseThread(foo.blah) = ", foo.blah

Метакласове в Python

„ Програмиране с Python“, ФМИ
Стефан Кънев & Николай Бачийски
11.04.2007г.
Метакласове

Метакласовете са инструмент, който ще ползвате в много малко случаи и клиента на вашия код винаги ще е друг програмист. Те помагат за създаване на по-устойчиви и използваеми абстракции, а не решават проблеми директно. Въпреки това, те имат големи възможности и можете да постигнете с тях много неща по елегантен начин. Имайте предвид, че всичко което можете да постигнете с метакласове може да стане и със стандартни способи.
Концепцията

Метакласовете представляват "типове" на класовете. Тези типове определят механиката и правилата на които класът се подчинява. Пример биха могли да бъдат класовете нов и стар стил, които работят по различни правила. Но като нищо може да си измислим и наши типове класове. Например:

* singleton-и
* класове, чиито методи при грешка просто връщат None
* класове, чиито методи получават self неявно

Singleton


class DatabaseConnector(object):
self = None
def __new__(cls):
if not DatabaseConnector.self:
DatabaseConnector.self = object.__new__(cls)
return DatabaseConnector.self

class Mailer(object):
self = None
def __new__(cls):
if not Mailer.self:
Mailer.self = object.__new__(cls)
return Mailer.self

mailer1 = Mailer()
mailer2 = Mailer()
print mailer1 is mailer2 # True

Същото, но с метакласове


class singleton(type):
def __new__(cls, name, bases, classDict):

def getInstance(klass):
if not klass.self:
klass.self = object.__new__(klass)
return klass.self

classDict['self'] = None
classDict['__new__'] = getInstance

return type.__new__(cls, name, bases, classDict)

class Mailer(object):
__metaclass__ = singleton

mailer1 = Mailer()
mailer2 = Mailer()
print mailer1 is mailer2

Класове, чиито методи не хвърлят грешки


def silent(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except:
return None
return wrapper

class Something(object):
@silent
def foo(self): assert false

@silent
def bar(self): print belch

@silent
def omgTooMuchDecorators(self): pass

something = Something()
something.foo()

Отново, с метакласове


def silent(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except:
return None
return wrapper

class silentType(type):
def __new__(cls, name, bases, classDict):
for name, attr in classDict.items():
if callable(attr):
classDict[name] = silent(attr)

return type.__new__(cls, name, bases, classDict)

class Something(object):
__metaclass__ = silentType
def foo(self): assert false
def bar(self): print belch
def omgTooMuchDecorators(self): pass

something = Something()
something.foo()
something.bar()

Класове без self


class Person(object):
__metaclass__ = selfless
def __init__(name, age):
self.name = name
self.age = age

def __repr__():
return "Person(%s, %d)" % (repr(self.name), self.age)

def sayHi():
print "Hello, I'm %s and I'm %d years old" % (self.name, self.age)

def rename(newName):
self.name = newName

Класове без self (2)


from types import FunctionType

class selfless(type):
def __new__(cls, name, bases, classDict):
for attr in classDict:
if not isinstance(classDict[attr], FunctionType): continue
classDict[attr] = selflessWrapper(classDict[attr])
return type.__new__(cls, name, bases, classDict)

def selflessWrapper(func):
def wrapper(self, *args, **kwargs):
hadSelf, oldSelf = func.func_globals.has_key('self'),
func.func_globals.get('self')
func.func_globals['self'] = self
returnValue = func(*args, **kwargs)
if hadSelf:
func.func_globals['self'] = oldSelf
else:
del func.func_globals['self']
return returnValue
wrapper.func_name = func.func_name + "_wrapper"
return wrapper

Пак Метакласове в Python

„ Програмиране с Python“, ФМИ
Стефан Кънев & Николай Бачийски
16.05.2007г.

[Metaclasses] are deeper magic than 99% of users should ever worry about. If you wonder whether you need them, you don't (the people who actually need them know with certainty that they need them, and don't need an explanation about why).

Tim Peters

Какво е метаклас?


Метаклас — клас, чийто инстанции са класове.


Забравете предишнoto твърдение.
Метапрограмиране

* Meta (from Greek: μετά = "after", "beyond", "with"), is a prefix used in English in order to indicate a concept which is an abstraction from another concept, used to complete or add to the latter.
* Програмиране за програмирането.
* Програмиране, при което клиентите са програмисти.
* При ООП:
o разширяване на езика с нови ”типове класове“
o промяна на природата на обектите
+ добавяне на функционалност (методи от метакласа например)
+ създаване на обекти, които приличат на класове (от които могат да се правят нови инстанции)
+ налагане на ограничения върху класовете
o автоматизиране на сложни задачи
+ регистриране на новосъздадени класове в някаква система

Метакласовете като инструмент за метапрограмиране

* Почти всичко може да се направи и без метакласове:
o factory класове
o функции, които обработват класа след създаването му
o функции, които регистрират новосъздадения клас в системата
* С метакласове просто е по-лесно и по-елегантно
* Използват се вътрешно в типовата система на Python

Употреба: регистрация/търсене

Искаме да видим всички класове, които са се регистрирали в нашата система.

Пример: имаме зоологическа градина с цяла йерархия от класове, като позволяваме на програмистите да дефинират нови класове от животни. Как да видим всички класове животни? Как да видим всичко животни, които могат да плуват? Как да намерим всички животни наследник на класа Риба?
Употреба: проверки при създаване на класове

* Гаранция, че класът удовлетворява определен интерфсейс
* Проверка за стил на кода:
o наличие на docstring-ове
o имена на методи — getName срещу get_name
o дълбочина на наследяването — ≤ 5
* Всякакви други проверки, които ще гарантират, че нмовосъздадения клас отговаря на изискванията на вашата система

Внимавайте да не сте много строги.
Употреба: създаване на специални класове

* обвиване на методи (pre/post conditions)
* добавяне, преименуване на методи
* зареждане на методи/родители от някъде:
o зависещи от системата: разширения (plugins)
o XML схеми и DTD

В Python

* при създаване на тялото на клас се гледа стойността на __metaclass__
* по подразбиране метакласа е type или types.ClassType (стар стил)
* метаклас може да се наследи и от родителски клас

Какво може/трябва да има един метаклас?

* (Мета)класовете са частен случай на обикновени обекти — с техните методи, свойства
* Всичко основно може да се наследи от type и за нас не остава кой знае колко за добавим
* Най-често се променят:
o Инициализатори: __new__, __init__
o Представяне на класовете като низове
o Атрибути и свойства на новия клас (включително методи:)
* Не могат да се ползват за промяна на логиката на:
o търсене в пространсвата от имена
o MRO (Method Resolution Order)
o Дескрипторите (за тях по-нататък)

Регулярни изрази в Python


„ Програмиране с Python“, ФМИ

28.02.2007г.

Идеята

Регулярните изрази се използват за търсене и заместване в текст. Те предлагат апарат, с който дефинирате шаблони, описващи последователност от символи, след което да проверявате дали даден текст съдържа съответния шаблон. Основават се на идея от дискретната математика, макар че реализацията им в езиците за програмиране е доста по-сложна.

Прост пример

  • на pam съответстват spam и pamela
  • на h(ot|ound) dog съответстват hot dog и hound dog
  • на fn\d+ съответстват fn1, fn44444, fn420169 и т.н.
  • на .at съответстват hat, cat, rat и т.н.
  • на [a-zA-Z0-9._]+@[a-aA-Z0-9._]+\.[a-z]+ съответстват повечете email адреси

В детайли

  • Регулярните изрази представляват текстови низове
  • Буквите и цифрите съответстват на себе си
  • Точката съответства на всеки символ, без новия ред
  • \d съответства на цифра
  • \w съответства на буква
  • \s съответства на бяло пространство
  • \D, \W и \S са инверсии на съответните символи

В детайли (2)

  • \d\d\d\d\d\d\d съответства на ЕГН
  • \w\w\w\d\w\w\d\d съответства на три букви, цифра, две букви и после две цифри (например ani4ka92)
  • fn\d\d\d\d\d съответства на част от факултетните номера
  • M\w\w\w\w\sT\w\w\sP\w\w\w\w\w би хванало Mityo The Python
  • s..m съответства на spam и sram

В Python

В Python се ползва модулът re

import re

>>> import re
>>> re.search('spam', 'gmail')
>>> re.search('s..m', 'spam')
<_sre.sre_match>
>>> re.search('\\d\\d', 'asnwer: 42')
<_sre.sre_match>

Още детайли

  • Операторът | в регулярните изрази означава това отляво или това отдясно
  • (c|h)at намира cat и hat
  • ^ съвпада с началото на низа. ^oo ще намери oop, но не и foo.
  • $ съвпада с края на низа. ar$ ще намери bar, но не и argh.

Повторения

  • ? означава, че да го има 0 или 1 пъти
  • + означава, че нещо може да го има един или повече пъти
  • * означава, че нещо може да го има нула или много пъти
  • {x, y} означава, че нещо може да го има от x до y пъти
Примери:
\d{8}

fn\d+
\d{1,2}\.\d{1,2}.\d{4}
route\d*

Класове

  • Класовете съответстват на някакво множество от символи.
  • Бележат се с [].
  • [chr]at съответства на cat, hat и rat.
  • Могат да включват и последователности от символи - [a-z0-8] съответства на малка буква или цифра, без 9
  • Ако започва с ^, класът се интерпретира като отрицание на символите вътре - [^A-Z] съответства на нещо, което не е голяма буква

Област

Върнатия от search обект има методи start, end и span.



>>> import re
>>> text = 'Bla bla bla mityo blabla bla'
>>> match = re.search(r'mityo', text)
>>> match.span()
(12, 17)
>>> match.start(), match.end()
12, 17
>>> text[match.start(), match.end()]
'mityo'

Групи

Всички изрази оградени със скоби се запазват в обекта върнат от re.search. Те се наричат групи



>>> import re
>>> match = re.search(r'\w+ (\w+), fn(\d+)',
'Nickolay Bachiiski, fn43600')
>>> match.group(1)
'Bachiiski'
>>> match.group(2)
'43600'

Субституции

Всички изрази оградени със скоби се запазват в обекта върнат от re.search. Те се наричат групи



>>> import re
>>> re.sub(r':(.*)!', r'!\1?', "I'm a :very big! hacker!")
"I'm a !very big hacker?"

Алчни "квантификатори"

+, * и {x,y} намират максималния брой символи, които могат. Това се нарича "алчно поведение" (greedy). Ако искате да намирате минималния брой, добавете едно ? към края



>>> import re
>>> re.sub(r'(.*)', r'\1',
'My name is Mityo the Python')
'My name is Mityo the Python'

>>> re.sub(r'(.*?)', r'\1',
'My name is Mityo the Python')
'My name is Mityo the Python'

Още за типовете и обектите в Python


„ Програмиране с Python“, ФМИ

Стефан Кънев & Николай Бачийски

14.05.2007г.

За какво ще става дума

  • Разпознаване на типове
  • Копиране на обекти
  • Наследяване на вградени типове

type

  • typeвръща специален обект от тип тип, стойност годна само за директни проверки
  • def update(val):
    if type(val) == type(0):
    _update(val)
    else:
    raise ValueError("Cannot update non-integer!")
  • >>> type('baba')
  • >>> type([])
  • >>> type(type('sugrug'))

Модулът types

  • Ако става дума за вграден тип, type връща обект, който може да бъде намерен в модула types

  • from types import *
    def update(val):
    if type(val) == IntType:
    _update(val)
    else:
    raise ValueError("Cannot update non-integer!")
  • В този модул може да намерите повечето вградени типове, като имената им са с главни букви и имат Type отзад: DictType, ListType, TupleType, BuiltinFunctionType и т.н.

Копиране на обекти

  • a = b не копира стойностите, а насочва a и b към един и същи обект!
  • Идиом за плитко копиране на списъци: newlist = l[:]
  • Още един идиом за плитко копиране: newlist = [x for x in l], прави същото като горния
  • names = 'a b c'.split()
    grades = [1, 2, 3]
    both = [names, grades]
    shallowCopy = both[:]
    names.append('d')
    print shallowCopy[0]
  • Така копираме елементите само на едно ниво.

Плитко копиране на произволни обекти

  • Не винаги си имаме работа със писъци, понякога не е толкова лесно да копираме един обект.
  • На помощ ни идва вградения модул copy
  • import copy
    class C:
    def __init__(self, l): self.l = l
    def __getitem__(self, i): return self.l[i]
    items = [1, 2, 3]
    c = C(items)
    shallow = copy.copy(c)
    print c[2], shallow[2]
    (3, 3)
    items.append(4)
    print c[3], shallow[3]
    (4, 4)
    shallow.l = [42]
    print c.l, shallow.l
    ([1, 2, 3, 4], [42])

Дълбоко копиране

  • Защо е необходимо?
  • import copy
    items = [1, 2, 3]
    d = dict(items=items)
    shallow = copy.copy(d)
    deep = copy.deepcopy(d)
    items.append(4)
    print shallow['items'], deep['items']

В картинки (1)

items = [1, 2, 3]
d = dict(items=items)
shallow = copy.copy(d)
deep = copy.deepcopy(d)

state and references before the items modification

В картинки (2)

items.append(4)

state and references after the items modification

Наследяване на вградени типове

  • Цел: създаване на обекти, които приличат на такива от някой вграден тип (речник, списък, низ)
  • Разширяване функционалността на вградени типове — речник, чийто ключове могат да се достъпват и като атрибути
  • Удобство — ако обектът ни има поведение на списък, по-добре да ползваме познатия писъчен протокол, вместо да измисляме нови методи

Без наследяване

  • Можем да постигнем целта си и без наследяване
  • Ако имате желание да дефинирате сами всички методи на list, огромна част, от които се припокриват като функционалност с тези от вградените списъци
  • Понякога е удачно: трябва ни само индексиране([], __getitem__) и не искаме да си замърсяваме класа с всички методи на list

Старият начин

Преди 2.3 не е било възможно стандартните класове като list, dict, str да се наследяват. Използвали са се няколко стандартни модула, които са предоставяли специални класове, които да бъдат наследявани:

  • UserDict.UserDict — речник, който не поддържа итерация (за съвместимост със стари версии)
  • UserDict.IterableUserDict — речник, който си поддържа и итерация
  • UserDict.DictMixin — добавя стандартните методи на класове, които вече поддържат стандартния интерфейс: __getitem__(), __setitem__(), __delitem__(), keys().
  • UserList.UserList
  • UserString.UserString
  • UserString.MutableString — малко по-различен низ, може да бъде променян в движение

Всички си имат по един атрибут data, в който се пазят реално данните.

Пример: речник с атрибути

class AttrDict(dict):
def __getattr__(self, attr):
if attr in self:
return self[attr]
else:
raise AttributeError("exists neither an attribute nor a key called '%s'." % attr)

if __name__ == "__main__":
d = AttrDict(a = 5, get = 6)
print d.a
print d.get
print d.baba

Частичен пример: низове с case-insensitive сравнения

def lowerFuncs(*names):
def lowerFunc(clsname, bases, classDict):
for name in names:
def comparator(self, other, *args, **kwargs):
return getattr(str, name)(self._lower, other.lower(), *args, **kwargs)
classDict[name] = comparator
return type(clsname, bases, classDict)
return lowerFunc

class CIStr(str):
__metaclass__ = lowerFuncs('__eq__', '__le__', '__lt__', '__ge__', '__gt__')

def __init__(self, *args, **kwargs):
str.__init__(self, *args, **kwargs)
# string will never change, so calc once
self._lower = self.lower()
def startswith(self, prefix, *args, **kwargs):
return str.startswith(self.__lower, prefix.lower(), *args, **kwargs)
def index(sub, *args, **kwargs):
return str.index(self.__lower, sub.lower(), *args, **kwargs)
… # и така нататък...

if __name__ == "__main__":
s = CIStr("Baba")
print s == "baba"

Сериализация в Python


„ Програмиране с Python“, ФМИ

Стефан Кънев & Николай Бачийски

11.04.2007г.

Проблем

Имаме нужда да пазим информация между две изпълнения на едно и също приложение. Искаме достъпа до тази информация да е лесен и да добавя минимално код в самото приложение. Нуждите на приложението са прости и няма да имаме нужда от конкурентен достъп или заключване на ресурси.

Решение

Сериализация се нарича възможността да представяме обекти като поредица от байтове. По този начин те могат да бъдат записвани във файловата система или изпращани през TCP/IP и реконструирани в последствие.

Pickle

pickle е модул, който може да сериализира прости Python обекти.

pickle.dump(object, file)
Приема отворен за писане файл file и Python обект object. Записва обекта в файла.
pickle.load(object)
Приема отворен за четене файл и прочита един обект, който е и резултат от функцията

Pickle (2)


from __future__ import with_statement
import pickle

with open('/home/aquarius/foo.txt', 'w') as file:
pickle.dump("The answer", file)
pickle.dump(["spam", "eggs", "ham"], file)

with open('/home/aquarius/foo.txt', 'r') as file:
print pickle.load(file)
print pickle.load(file)

Какво представлява сериализацията?

Pickle сериализира обектите с един от три различни протокола. Протокола по подразбиране представя обектите като ASCII низове. Функциите dumps и loads връщат сериализирана форма вместо да я записват във файл.


import pickle

serializedList = pickle.dumps(["spam", "eggs", "ham"])
print repr(serializedList)
"(lp0\nS'spam'\np1\naS'eggs'\np2\naS'ham'\np3\na."

Какво може да се сериализира?

  • True, False и None
  • int, long, float и complex
  • str и unicode
  • Списъци, речници, множества и n-орки, които съдържат само обекти, които могат да се сериализират
  • Функции и класове, дефинирани в корена на модула
  • Вградени функции, дефинирани в корена на модула

Сериализиране на един обект два пъти

Ако сериализирате сложна структура, в която един обект присъства два пъти (по адрес), pickle ще го сериализира еднократно.


import pickle

primes = [2, 3, 5, 7, 11, 13]
answer = 42
things = [primes, answer, primes]
print things[0] is things[2] # True

serialized = pickle.dumps(things)
newThings = pickle.loads(serialized)
print newThings[0] is newThings[2] # True

print newThings[0] is things[0] # False

Pickler и Unpickler

Имате достъп до същата функционалност под формата на класове.


from __future__ import with_statement
import pickle

with open('/home/aquarius/foo.txt', 'w') as file:
pickler = pickle.Pickler(file)
pickler.dump('Under the Violet Moon')
pickler.dump('Soldier of Fortune')
pickler.dump('Colubrid on the Tree')

with open('/home/aquarius/foo.txt') as file:
unpickler = pickle.Unpickler(file)
print unpickler.load()
print unpickler.load()
print unpickler.load()

cPickle

cPickle e реализация на pickle на C, което я прави доста по-бърза.


from __future__ import with_statement
import cPickle

with open('/home/aquarius/foo.txt', 'w') as file:
cPickle.dump("The answer", file)
cPickle.dump(["spam", "eggs", "ham"], file)

with open('/home/aquarius/foo.txt', 'r') as file:
print cPickle.load(file)
print cPickle.load(file)

cPickle (2)

За сравнение

import pickle
import cPickle
from timeit import Timer

fancy = [[x ** 2 for x in range(0, 1000)], ["spam", "ham", "eggs"],
["-" * i for i in range(0, 1000)],]

def pickleWith(pickler):
serialized = pickler.dumps(fancy)
restored = pickler.loads(serialized)

print "pickle: ", Timer("pickleWith(pickle)", "from __main__ import *").timeit(50)
print "cPickle: ", Timer("pickleWith(cPickle)", "from __main__ import *").timeit(50)

# pickle: 3.28504800797
# cPickle: 0.722439050674

shelve

shelve е сравнително прост модул, който позволява да записвате данни във файл под формата на речник. Можете да ги достъпвате в последствие по ключовете, с които сте ги записали. Всички обекти се сериализират посредством pickle, докато организацията им във файла става посредством dbm, gdbm или berkeley db.

shelve (2)


import shelve

db = shelve.open('/home/aquarius/foo.db')
db['name'] = 'Mityo the Python'
db['age'] = 33
db['favouriteBands'] = ["Blackmore's Night", "Deep Purple", "Rainbow"]
db['favouriteSong'] = "Colubrid on the Tree"
db.close()

db = shelve.open('/home/aquarius/foo.db')
print db.keys()
print db['name']
print db['favouriteBands']
print db['favouriteSong']
db.close()

Още тестване и документация - Python


„ Програмиране с Python“, ФМИ

Стефан Кънев & Николай Бачийски

16.02.2007г.

Примерно пакетче с модули

Нека разгледаме модула world

  • __init__.py
  • destroyer.py
  • god.py

Прости тестове: test_world.py

  • клас наследник на unittest.TestCase
  • всеки негов метод започващ с test е отделен сценарий
  • unittest.main се грижи да намери подходящите класове и да извика подходящите им методи
  • просто!

setUp и tearDown

  • понякога се налага да правим едно и също преди и след някой сценарий (метод на TestCase)
  • методът setUp на TestCase ще се извика преди всеки сценарий
  • методът tearDownще се извика след всеки сценарий
  • def setUp(self):
    self.w = world.World()
    def tearDown(self):
    self.w = None

Специален TestCase

  • world.destroyer.do отпечатва разни неща, които не искаме да виждаме по време на тестовете
  • можем да си направим наследник на unittest.TestCase, който има желаната функционалност
  • и ползваме негов наследник в случаите, в които ни трябва тази функционалност
  • Пример: world.test.testcases

Под капака на unittest

  • TestLoader
  • TestSuite
  • TestResult
  • TextTestRunner
  • Пример: world.test

Документация—Интернет

  • http://docs.python.org/ — обновена, удобна, ако знаете къде да търсите, това което ви трябва
    • Library Reference—систематизирано описание на функции, модули
    • Global Module Index—азбучен списък с подробни описания на модулите
    • Language Reference—синтактични правила, твърдения, оператори
  • често най-бързия начин да намерите, каквото ви е нужно е търсене в Google за python <функция/метод/клас/…>

Документация—на вашия компютър

  • Под Windows—Python Manuals в chm формат. Може да се търси, на практика в него е цялата HTML документация.
  • HTML документация—почти същата като на сайта (малко по-стара вероятно) и й лиспва търсене. Ще я намерите на собствения си компютър някъде, освен ако за вашата ОС не се разпространява отделно от самия Python.

pydoc

  • Под Windows: python.exe pydoc.py Трябва да сложите в пътя директориите PythonX.Y и PythonX.Y/Lib.
  • На другите места: pydoc
  • pydoc <име>—търси документация за тази функция, тема, модул ключова дума…
  • pydoc -k <ключова дума>—търси тази дума в описанията на всички модули
  • pydoc -p <порт>—пуска HTTP сървър на вашия компютър и можете да разглеждате HTML документацията като отворите с браузъра си http://localhost:<порт>/
  • pydoc -g—отваря графична програма, в която може да търсите по ключова дума

Документация на наши модули

  • Помните ли docstring-овете?
  • def f(x, y):
    """f makes you happy putting a x into your y."""
  • Стига вашия модул да се намира някъде в пътя за търсене на Pythоn
    • можете да погледнете в sys.path
    • PYTHONPATH
    • модулът site
  • при разглеждане с pydoc ще се вижда и документацията на вашите модули
  • pydoc -w —записва документация на диска в HTML формат

epydoc

  • гъвкава система за генериране на документация
  • поддържа много формати
  • не е в стандартната Python дистрибуция