Когато пишете код, рядко го правите самоцелно. Вместо това, вие опитвате се да решите някакъв реален проблем със собствена логика, терминология и особености. Винаги когато пишете код трябва да се стараете той да отразява много добре този проблем - това го прави много по-четим, по-лесен за поддръжка и по-разбираем от външни хора. Още повече, така вие ясно показвате намерението което вашия код има, вместо да карате читателя да задълбава в особенностите на вашата реализация.
Променливите обикновенно отговарят за съществуващи обекти/концепции в реалния проблем, който решавате. Това ги прави идеални за комуникиране на идеята на кода. За целта, обаче, се налага да избирате смислени имена.
# Грешно temp = sqrt(b ** 2 - 4 * a * c) x1 = (-b + temp) / (2 * a) x2 = (-b - temp) / (2 * a) # По-правилно discriminant = sqrt(b ** 2 - 4 * a * c) x1 = (-b + discriminant) / (2 * a) x2 = (-b - discriminant) / (2 * a)
old = readOld() tupple = getValues("c:/") tup = {} for t in tupple: if old[t] != tupple[t]: continue tup.update({t:tupple[t]}) show(tup) save(tupple)
oldHashsums = readCachedHashsums() newHashsums = findHashsums('c:/') changedFiles = {} for filename in oldHashsums: if oldHashsums[filename] != newHashsums[filename]: changedFiles[filename] = newHashsums[filename] reportChanges(changedFiles) saveHashsums(newHashsums)
Рутините са едно от най-често използваните средства в програмирането. И все пак, причините за които има смисъл да създавате рутина са.
При именуване на рутини се съобразявайте внимателно със следните неща.
„Кохезията“ на една рутина смътно описва действието й. Като говорим за „добра кохезия“ имаме предвид, че една рутина прави едно единствено нещо и го прави добре. Най-силния вид „кохезия“ е функционалната. Други видове са:
Неприемлив вид кохезия
Действието на една рутина не трябва да зависи от стойностите на неин аргумент.
Лош подход.
def storePerson(name, age, address): if not isinsntace(name, str) or len(name) < 6: raise ValueError("Illegal name") if not isinstance(age, int, long) or age < 0: raise ArithmeticError("Illegal age") if not hasattr(address, "street") or not hassattr(address, "number"): raise ValueError("Illegal address") storeData(name, age, address.street, address.number)
По-добър подход.
def storePerson(name, age, address): assert long(age) >= 6 assert isinstance(name, str) and len(name) >= 6 storeData(name, age, address.street, address.number)
Когато пиеше функции, не ползвайте глобални променливи. Ама въобще. Най-честия случай на неправилно ползване на глобавни променливи е когато се употребяват за комуникация между функции. Никога не правете това. В рядките случаи, в които имате нужда от „глобални“ обекти правете Singleton-и или thread.local
-и.
В Python няма goto. Ако случайно пишете на друг език, в който има goto, това правило остава - не ползвайте goto.
Обектно-ориентираното програмиране ни позволява да моделираме концепции от външния свят в структури, разбираеми за машината. Това се нарича създаване на абстрактни типове на данни.
class Polynomial(object): def __init__(self, *cfs): ... def order(self): ... ... a = Polynomial(1, 4, 2) # x^2 + 4x + 2 print a[3], a[2], a[1], a[0] # 0 1 4 2 print a.order() # 2 b = Polynomial(1, 1, 4, 1) # x^3 + x^2 + 4x + 1 c = b - a # x^3 - 1 d = Polynomial(1, 0, 0, 1) # x^3 + 1 e = c * d # x^6 - 1
Всеки обект може да се разглежда като сбор от две неща:
Интерфейс:
y = power(x, a)
Имплементация:
def power(x, a): return 1 if a == 0 else power(x, a - 1) * x
def power(x, a):
if a == 0: return 1
r = power(x, a/2)
return x * x * (x if a % 2 else 1)
def power(x, a): return reduce(lambda a, b: a * b, [x] * a)
def power(x, a):
r = 1
for _ in range(a): r *= x
return x
def power(x, a): return x ** a
Има различни начини, по които може да представим абстрактната концепция в примитивите на езика.
(1, 2, 3) -> x^2 + 2x + 3
(3, 1) -> 3x + 1
((1, 2), (2, 1), (3, 0)) -> x^2 + 2x + 3
((1, 1), (2, 1), (1, 0)) -> 3x + 1
{2: 1, 1: 2, 0: 3} -> x^2 + 2x + 3
{1: 3, 0: 1} -> 3x + 1
Обикновено има разлика между множеството от стойности, които структурата на представянето може да приеме и множеството от стойности, които имат смисъл. Последните се наричат инвариант на представянето (representation invariant)
Например ако моделирате интервал от цели числа, може да го представите вътрешно като наредена двойка (лява граница, дясна граница)
. Съответно, (2, 6)
е смислено представяне, но (6, 2)
- не.
Функцията (в математически смисъл) съпоставяща инварианта на представянето на множеството от моделирани концепции се нарича абстрактна функция на този абстрактен тип.
Като Дарт Вейдър, само дето накрая убива Люк, а не императора.
Методите на един обект могат да се разделят на два вида:
int
, tuple
, str
, float
, long
, complex
и т.н.Имате два обекта. Те са равни ако…
…с произволна поредица от observer методи не може да разберете дали те са различни или не.
…с произволна поредица от observer-и и мутатори не може да разберете дали те са различни или не.
За всеки метод се дефинира следното:
class Rectangle: def a(self): ... def b(self): ... def setA(self, a): ... def setB(self, b): ... class Square(self): def a(self): ... def setSide(self, side): ...
Клас Б може да наследи от клас А, само ако на всички места на които може да използвате инстанция на А може да използвате инстанция на Б.
В термините на Design by Contract:
Б може да наследи А ако:
Един метод може да праща съобщения на:
self
или:
**Не говори с непознати**
The second coming of Jesus.
Има смисъл в Python