2012-04-05 3 views
6

Я создаю пакеты python/MyLibPackage, которые я буду импортировать в свои проекты.Как перезаписать импортированный класс python для всех вызовов

MyLibPackage.____init____.py включает mymodiciation.py. Кроме того, папка MyLibPackage содержит другой файл: base_classes.py (= внешний проект)

mymodiciation.py импорт "from base_classes import *".

Цель: Я могу импортировать MyLibPackage, который имеет все классы из base_classes (= внешний проект). И если мне нужно модифицировать некоторые классы или функции, я могу перезаписать это в mymodiciation.py. Это работает, но у меня проблема. Например:

Я перезаписать эти классы в mymodiciation.py:

class Bookcollection(Bookcollection): 
    new_member = "lalala" 


class user(user): 
    def get_books(self): 
     return Bookcollection() 

если я:

from MyLibPackage import * 
x = user() 
books = x.get_books() 

тогда объект Bookcollection имеет свойство "new_member". Хорошо! Но если я буду делать это:

from MyLibPackage import * 
x = shelf() #this class is not overwritten and used also the object "Bookcolelction" 
books = x.get_books() 

тогда объект Bookcollection имеет НЕ свойство «new_member», потому что он инстанс с MyLibPackage.base_classes.Bookcollection, а не с моим перезаписан классом MyLibPackage.mymodiciation.Bookcollection

Как я могу сказать: если я переписываю класс в mymodiciation, то MyLibPackage должен использовать это, хотя при вызове cames из MyLibPackage.base_classes.shelf (get_books).

+0

Если вы запутались в цепях наследования, то, возможно, вам стоит взглянуть на некоторые другие решения. – jldupont

+0

Возможно, я мог бы попытаться работать с крючками ?! –

ответ

11

Что вы хотите сделать, называется «обезглавливание обезьян» и имеет мало общего с Ориентацией объектов.

Python действительно поддерживает его, но у вас есть контроль над всеми вашими классами, вы должны серьезно рассмотреть свой проект, чтобы проверить, действительно ли вам это нужно.

Возможно, используя инфраструктуру, такую ​​как Zope Component Architecture, которая позволяет вам маркировать классы с интерфейсами и предоставляет объекты адаптера, чтобы вы могли чисто использовать один объект с некоторым интерфейсом, на котором он не был создан на первом месте, будет лучшая идея.

Таким образом, то, о чем вы просите, это изменить класс в другом модуле, где он есть, - чтобы изменения были видны для всех других модулей.

Вы делаете именно это: измените класс в модуле, в котором он находится. В Python это может быть сделано просто приписывать свой новый класс к нужному имени в модуле происхождения:

import base_classes 

class Bookcollection(base_classes.Bookcollection): 
    new_member = "lalala" 

base_classes.Bookcollection = Bookcollection 

(настоятельно рекомендуются, чтобы избежать «от й импорта *» в любом проекте больше, чем один скрипт - в этом случае у вас было 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. И независимо от того, какой подход вы принимаете, do пишите модульные тесты.

+0

Спасибо за это. Решил проблему, которую я имел! – ehambright

1

Наличие классов с таким же именем, как правило, является плохой идеей, поскольку вы получаете столкновения пространства имен. Я бы рекомендовал изменить ваш MyLibPackage.base_classes.Bookcollection на MyLibPackage.base_classes.BaseBookcollection или аналогичный. Затем это должно работать так, как ожидалось.

+0

Но как это должно работать, когда мне нужно изменить только некоторые части базового слоя? Для exmaple - функция. Остальная часть base_class.py работает с его пространствами имен. Вызовы в остальной части base_classes не знают о моих новых производных классах –

+0

Итак, переименуйте свой производный класс в BookcollectionImproved или сделайте что-то вроде 'from base_classes import Bookcollection как BaseBookcollection'! – phobie

Смежные вопросы