У меня есть класс, который предоставляется мне внешней библиотекой. Я создал подкласс этого класса. У меня также есть экземпляр исходного класса.
Теперь я хочу превратить этот экземпляр в экземпляр моего подкласса без изменения каких-либо свойств, которые уже имеет экземпляр (кроме тех, которые мой подкласс все равно переопределяет).
Кажется, что работает следующее решение.
# This class comes from an external library. I don't (want) to control
# it, and I want to be open to changes that get made to the class
# by the library provider.
class Programmer(object):
def __init__(self,name):
self._name = name
def greet(self):
print "Hi, my name is %s." % self._name
def hard_work(self):
print "The garbage collector will take care of everything."
# This is my subclass.
class C_Programmer(Programmer):
def __init__(self, *args, **kwargs):
super(C_Programmer,self).__init__(*args, **kwargs)
self.learn_C()
def learn_C(self):
self._knowledge = ["malloc","free","pointer arithmetic","curly braces"]
def hard_work(self):
print "I'll have to remember " + " and ".join(self._knowledge) + "."
# The questionable thing: Reclassing a programmer.
@classmethod
def teach_C(cls, programmer):
programmer.__class__ = cls # <-- do I really want to do this?
programmer.learn_C()
joel = C_Programmer("Joel")
joel.greet()
joel.hard_work()
#>Hi, my name is Joel.
#>I'll have to remember malloc and free and pointer arithmetic and curly braces.
jeff = Programmer("Jeff")
# We (or someone else) makes changes to the instance. The reclassing shouldn't
# overwrite these.
jeff._name = "Jeff A"
jeff.greet()
jeff.hard_work()
#>Hi, my name is Jeff A.
#>The garbage collector will take care of everything.
# Let magic happen.
C_Programmer.teach_C(jeff)
jeff.greet()
jeff.hard_work()
#>Hi, my name is Jeff A.
#>I'll have to remember malloc and free and pointer arithmetic and curly braces.
Однако я не уверен, что это решение не содержит никаких оговорок, о которых я не думал (извините за тройственное отрицание), тем более, что переназначение магического __class__
просто не кажется правильным. Даже если это сработает, я не могу не почувствовать, что должен быть более питонический способ сделать это.
Есть ли?
Изменить: Спасибо всем за ваши ответы. Вот что я получаю от них:
-
Хотя идея переклассификации экземпляра путем присвоения
__class__
не является широко используемой идиомой, большинство ответов (4 из 6 на момент написания) считают ее действительным подходом. Один anwswer (by ojrac) говорит, что это "довольно странно на первый взгляд", с которым я согласен (это было причиной для того, чтобы задать вопрос). Только один ответ (Джейсон Бейкер, с двумя положительными комментариями и голосами) активно отговаривал меня от этого, однако делал это на примере примера использования примера moreso, чем в технике вообще. -
Ни один из ответов, будь то положительный или нет, находит фактическую техническую проблему в этом методе. Небольшим исключением является jls, который упоминает о том, чтобы остерегаться классов старого стиля, что, скорее всего, верно, и расширений C. Я полагаю, что C-расширения, поддерживающие новый стиль, должны быть такими же прекрасными, как и сам Python (предполагая, что последнее верно), хотя, если вы не согласны, продолжайте отвечать.
Что касается вопроса о том, как это pythonic это, было несколько положительных ответов, но реальных причин не было. Глядя на Zen (import this
), я думаю, что наиболее важным правилом в этом случае является "Явное лучше, чем неявное". Я не уверен, однако, говорит ли это правило за или против переклассификации таким образом.
-
Использование
{has,get,set}attr
кажется более явным, поскольку мы явно вносим изменения в объект вместо использования магии. -
Использование
__class__ = newclass
кажется более явным, потому что мы явно говорим: "Это теперь объект класса newclass," ожидают другого поведения "вместо молчащих изменений атрибутов, но оставляя пользователей объекта, полагая, что они имеют дело с обычный объект старого класса.
Подводя итог: с технической точки зрения, метод выглядит нормально; вопрос о питоничности остается без ответа с предубеждением к "да".
Я принял ответ Мартина Гейслера, потому что пример плагина Mercurial является довольно сильным (а также потому, что он ответил на вопрос, который я даже еще не задал себе). Однако, если есть какие-либо аргументы по вопросу о pythonicity, я все равно хотел бы их услышать. Спасибо всем до сих пор.
P.S. Фактический прецедент - это объект управления данными UI, который требует увеличения функциональности во время выполнения. Однако вопрос должен быть очень общим.