Метакласове

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

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

Метакласове

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

Концепцията

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

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

pijo = Person(u'пижо', 11)
pijo.sayHi()
pijo.rename(u'пендо')
pijo.sayHi()