Ответ 1
То, что вы хотите сделать, называется "исправление обезьян" и не имеет ничего общего с объектной ориентацией.
Python поддерживает его, но у вас есть контроль над всеми вашими классами, вы должны серьезно пересмотреть свой проект, чтобы проверить, действительно ли он вам нужен.
Может быть, лучше использовать такую инфраструктуру, как Zope Component Architecture, которая позволяет помечать классы интерфейсами и предоставляет объекты-адаптеры, чтобы вы могли без проблем использовать один объект, поскольку для него имеется некоторый интерфейс, для которого он изначально не был предназначен.
Тем не менее, вы просите изменить класс в другом модуле, где он находится, - чтобы изменения были видны всем остальным модулям.
Вы делаете именно это: меняете класс в модуле, к которому он принадлежит. В Python это можно сделать, просто присвоив вашему новому классу желаемое имя в модуле происхождения:
import base_classes
class Bookcollection(base_classes.Bookcollection):
new_member = "lalala"
base_classes.Bookcollection = Bookcollection
(Чтобы подобные вещи работали, вы должны избегать "из x import *" в любом проекте, превышающем один скрипт - в этом случае у вас было 2 переменные с одинаковым именем и разными значениями в коде: основа класс и унаследованный класс, например. Пространства имен Python позволяют избежать этого).
Таким образом, это изменит класс Bookcollection в модуле base_class - НО только для кода, который будет ссылаться на него с этого момента и далее в вашей цепочке выполнения. Если класс "x" в вашем примере определен в модуле "base_classes" или определен иным образом до импорта "MyModule", он получит ссылку на старый класс "Bookcollection".
Как вы можете видеть, это может быстро превратиться в беспорядок, и если вы действительно выберете этот подход, единственный способ сохранить ваш проект даже пригодным для использования - это провести модульные тесты, чтобы убедиться, что все классы, которые вы хотите пропатчить, действительно пропатчены. Как вы видите, даже порядок импорта модулей будет иметь значение. Если у вас есть места для тестов, они будут повреждены, если вы сделаете импорт в порядке, который нарушает ваши исправления обезьяны.
Если вам просто нужно добавить и заменить вещи в существующем классе, вы можете обезьяны исправить сам класс, чтобы заменить его компоненты, вместо того, чтобы обезопасить модуль, в котором он находится, чтобы заменить класс. Таким образом, порядок импорта модулей не будет иметь большого значения - он повлияет даже на существующие экземпляры этого класса:
import base_classes
base_classes.Bookcollection.new_member = "lalala"
def new_bookcol_method(self):
pass
# to replace or register a method in the other class:
base_classes.Bookcollection.__dict__["old_bookcol_method"] = new_bookcol_method
Это даст вам более согласованное поведение, чем попытка назначить новый класс (который сам по себе является объектом) тому же имени в исходном модуле.
В общем, вы должны либо сделать, как подсказывает @jamesj в своем ответе, и использовать разные классы, либо, если вам нужно динамическое поведение, использовать для этого поддерживаемую среду, например Zope Component Architecture. И какой бы подход вы ни выбрали, пишите юнит-тесты.