събота, 17 януари 2009 г.
Нишки в 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
Стефан Кънев & Николай Бачийски
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
Стефан Кънев & Николай Бачийски
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 Pythons..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)
В картинки (2)
items.append(4)
Наследяване на вградени типове
- Цел: създаване на обекти, които приличат на такива от някой вграден тип (речник, списък, низ)
- Разширяване функционалността на вградени типове — речник, чийто ключове могат да се достъпват и като атрибути
- Удобство — ако обектът ни има поведение на списък, по-добре да ползваме познатия писъчен протокол, вместо да измисляме нови методи
Без наследяване
- Можем да постигнем целта си и без наследяване
- Ако имате желание да дефинирате сами всички методи на
list, огромна част, от които се припокриват като функционалност с тези от вградените списъци - Понякога е удачно: трябва ни само индексиране(
[],__getitem__) и не искаме да си замърсяваме класа с всички методи наlist
Старият начин
Преди 2.3 не е било възможно стандартните класове като list, dict, str да се наследяват. Използвали са се няколко стандартни модула, които са предоставяли специални класове, които да бъдат наследявани:
UserDict.UserDict— речник, който не поддържа итерация (за съвместимост със стари версии)UserDict.IterableUserDict— речник, който си поддържа и итерацияUserDict.DictMixin— добавя стандартните методи на класове, които вече поддържат стандартния интерфейс:__getitem__(),__setitem__(),__delitem__(),keys().UserList.UserListUserString.UserStringUserString.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иNoneint,long,floatиcomplexstrи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__.pydestroyer.pygod.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
TestLoaderTestSuiteTestResultTextTestRunner- Пример:
world.test
Документация—Интернет
- http://docs.python.org/ — обновена, удобна, ако знаете къде да търсите, това което ви трябва
- Library Reference—систематизирано описание на функции, модули
- Global Module Index—азбучен списък с подробни описания на модулите
- Language Reference—синтактични правила, твърдения, оператори
- често най-бързия начин да намерите, каквото ви е нужно е търсене в Google за
python <функция/метод/клас/…>- Пример: python unittest
Документация—на вашия компютър
- Под 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 дистрибуция