Найти - Пользователи
Полная версия: Обратное наследование
Начало » Python для экспертов » Обратное наследование
1 2 3 4 5
Ed
А так?
class Factory(object):

_IMPLEMENTATIONS = {}

def __init__(self, selector):
self.selector = selector

@classmethod
def register_implementation(cls, impl):
cls._IMPLEMENTATIONS[impl.im_class.name] = impl

def __getattr__(self, name):
return self._IMPLEMENTATIONS[self.selector(name)]

class DefaultAvatar:
name = 'default'
def get_avatar_path(self, user, size=48):
return '/static/avatars/%s/%d' % (user.login, size,)

class Gravatar:
name = 'gravatar'
def get_avatar_path(self, user, size=48):
return 'http://www.gravatar.com/avatar/bla-bla?'


implementation = 'default'
def implementation_selector(name):
return implementation

class User(object):
avatar = Factory(implementation_selector)
def __init__(self, login, email):
self.login = login
self.email = email
def get_avatar_path(self, size=48):
return self.avatar.get_avatar_path(self, size)

# --------

defavatar = DefaultAvatar()
Factory.register_implementation(defavatar.get_avatar_path)
gravatar = Gravatar()
Factory.register_implementation(gravatar.get_avatar_path)

user1 = User('login', 'test@example.com')
user2 = User('test2', 'test2@example.com')

print user1.get_avatar_path()
print user2.get_avatar_path()

implementation = 'gravatar'

print user1.get_avatar_path()
print user2.get_avatar_path()
zheromo
Согласен.
_IMPLEMENTATIONS лучше определить внутри класса фабрики.

Но __call__ __getattr__ лучше оставить, потому что в реализация может перекрывать больше чем один метод.

class Factory(object):

_IMPLEMENTATIONS = {}

def __init__(self, selector):
self.selector = selector

@classmethod
def register_implementation(cls, impl):
cls._IMPLEMENTATIONS[impl.name] = impl

def __call__(self):
return self._IMPLEMENTATIONS[self.selector(self.__class__)]

def __getattr__(self, name):
return getattr(self(), name)


class DefaultAvatar:
name = 'default'
def get_avatar_path(self, user, size=48):
return '/static/avatars/%s/%d' % (user.login, size,)
def get_avatar_something(self, user):
return 'something'

class Gravatar:
name = 'gravatar'
def get_avatar_path(self, user, size=48):
return 'http://www.gravatar.com/avatar/bla-bla?'
def get_avatar_something(self, user):
return 'gravatar'


implementation = 'default'
def implementation_selector(cls):
return implementation

class User(object):
avatar = Factory(implementation_selector)
def __init__(self, login, email):
self.login = login
self.email = email
def get_avatar_path(self, size=48):
return self.avatar.get_avatar_path(self, size)
def get_avatar_something(self):
return self.avatar.get_avatar_something(self)

# --------

defavatar = DefaultAvatar()
Factory.register_implementation(defavatar)
gravatar = Gravatar()
Factory.register_implementation(gravatar)

user1 = User('login', 'test@example.com')
user2 = User('test2', 'test2@example.com')

print user1.get_avatar_path()
print user1.get_avatar_something()
print user2.get_avatar_path()
print user2.get_avatar_something()

implementation = 'gravatar'

print user1.get_avatar_path()
print user1.get_avatar_something()
print user2.get_avatar_path()
print user2.get_avatar_something()
Еще возникла такая идея, для того чтобы базовый класс совсем ничего не знал о том какие фабрики и для чего он должен иметь построить структуру типа этой:

           BaseClass
/ / |
/ / |
/ / |
Imp1 Impl2 | --- Factory
\ \ |
\ \ |
\ \ |
ReallyUsedClass
Фабрика будет создавать новые инстансы базового класса на основе добавленных в нее классов.

Прототип:
class Factory(list):
def __init__(self, BaseClass):
self.name = BaseClass.__name__ + "Factory"
self = [BaseClass]
def __call__(self, *argv, **kw):
cls = type(self.name, tuple(reversed(self)), {})
return cls(*argv, **kw)

# ----

class _User(object):
def __init__(self, login, email):
self.login = login
self.email = email
def method(self):
return 'original method'
def get_avatar_path(self, size=48):
return '/static/avatars/%s/%d' % (self.login, size,)
def do_something(self):
return 'do something'

User = Factory(_User)

# ----

class GravatarUser(_User):
def get_avatar_path(self, size=48):
return '/gravatar!!!'
User.append(GravatarUser)

class SomethingUser(_User):
def do_something(self):
return 'new do something!!!'
User.append(SomethingUser)

# --- используем где-то там далеко ---

u = User('test', 'test@example.com')
print u.get_avatar_path(96)
print u.method()
print u.do_something()
Ed
zheromo
Но __call__ __getattr__ лучше оставить, потому что в реализация может перекрывать больше чем один метод.
Любой каприз :)
class Factory(object):

_IMPLEMENTATIONS = {}

def __init__(self, selector):
self.selector = selector

@classmethod
def register_implementation(cls, impl):
cls._IMPLEMENTATIONS[(impl.im_class.name, impl.__func__.__name__)] = impl

def __getattr__(self, name):
return self._IMPLEMENTATIONS[(self.selector(name), name)]

class DefaultAvatar:
name = 'default'
def get_avatar_path(self, user, size=48):
return '/static/avatars/%s/%d' % (user.login, size,)
def foo(self):
return 'default avatar foo'

class Gravatar:
name = 'gravatar'
def get_avatar_path(self, user, size=48):
return 'http://www.gravatar.com/avatar/bla-bla?'
def foo(self):
return 'gravatar foo'


implementation = 'default'
def implementation_selector(name):
return implementation

class User(object):
avatar = Factory(implementation_selector)
def __init__(self, login, email):
self.login = login
self.email = email
def get_avatar_path(self, size=48):
return self.avatar.get_avatar_path(self, size)

def foo(self):
return self.avatar.foo()

# --------

defavatar = DefaultAvatar()
Factory.register_implementation(defavatar.get_avatar_path)
Factory.register_implementation(defavatar.foo)
gravatar = Gravatar()
Factory.register_implementation(gravatar.get_avatar_path)
Factory.register_implementation(gravatar.foo)

user1 = User('login', 'test@example.com')
user2 = User('test2', 'test2@example.com')

print user1.get_avatar_path()
print user2.get_avatar_path()
print user1.foo()
print user2.foo()

implementation = 'gravatar'

print user1.get_avatar_path()
print user2.get_avatar_path()
print user1.foo()
print user2.foo()
Ed
Идея с неявными манипуляциями с классами мне не нравится. Тоже как-то сложно выглядит и непонятно какая от нее польза. Explicit is better than implicit.
zheromo
Формально код очень простой

class Factory(list):
def __init__(self, BaseClass):
self.name = BaseClass.__name__ + "Factory"
self = [BaseClass]
def __call__(self, *argv, **kw):
cls = type(self.name, tuple(reversed(self)), {})
return cls(*argv, **kw)
С помощью append мы добавляем классы которые “правильно” наследуют от базового. А __call__ фабрикует нам новый инстанс класса который наследует от них. Т.е. как будто бы мы написали:

def user_factory(login, email):
class UserMixIn(SomethingUser, GravatarUser, User):
pass

user = UserMixIn(login, email)
return user
Андрей Светлов
Слежу за вашей перепиской - и до сих пор ничего не понял.
Еще раз, простыми словами, объясните пожалуйста, что вы хотите сделать.
Вижу лишь занятные способы работы с классами, не более. Цель - ускользает от понимания.
zheromo
Цель проста:
1. есть некий набор классов К который не меняется
2. часть системы А использует набор К инстанцируясь от него
3. другая часть системы Б должна изменить поведение классов К, так чтобы это отразилось на А
4. ни А ни Б о друг друге не знают
5. Б взаимодействует с К до того как А будет инстанцирована
Ed
Окончательная цель, как я понял - сделать механизм динамической замены одних методов на другие в зависимости от неких внешних условий.
В последних примерах в зависимости от переменной implementation во всех объектах класса User при вызове get_avatar_path()
будет вызываться либо DefaultAvatar().get_avatar_path, либо Gravatar().get_avatar_path.
zheromo
Ed
Окончательная цель, как я понял - сделать механизм динамической замены одних методов на другие в зависимости от неких внешних условий.
Абсолютно верно
zheromo
Стратегия бы вполне подошла, но суть в том, что динамическая замена одних методов на другие может производится из нескольких мест, т.е. требуется некая “смешанная” стратегия, а не просто выбор одной. Т.к. разные части кода могут менять разные методы.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB