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

Още за типовете и обектите в 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"

Няма коментари:

Публикуване на коментар