2016-08-15 3 views
5

Я столкнулся с головоломкой. Я думаю, что должен знать, как это решить - я очень осведомлен и опыт в Python, но я не могу понять это. Кажется, что то, с чем я борюсь, должно быть обращено с помощью шаблона дизайна, который является вариацией на заводскую тему, но я не могу его вспомнить. (В конце этой записи я предлагаю техническое решение, но это кажется неправдоподобным). Надеюсь, вы сочтете эту дискуссию интересной сама по себе, но я с нетерпением жду некоторых предложений по решению моей проблемы.Как ограничить создание объектов одного класса экземплярами другого в Python?

Данные классы, A, B, C и D, я хочу ограничить создание экземпляров B методами экземпляров A, C методам B и D экземпляров методам C - другими словами, экземпляры фабрики для экземпляров B, B - экземпляры для экземпляров C, а C - фабрики для экземпляров D. (Я также хочу, чтобы каждый экземпляр D сохранял экземпляр C, созданный им, каждый экземпляр C, чтобы сохранить экземпляр B, созданный им, и каждый экземпляр B для хранения экземпляра A, созданного им, но это легко выполняется каждый экземпляр представляет себя как аргумент __init__, когда он создает экземпляр следующего класса вниз в иерархии.)

Это классы модельного уровня, которыми управляет приложение. Нет никаких проблем с приложением, управляющим экземплярами B, C или D, просто не следует создавать их напрямую - все, что должно быть в состоянии создать напрямую, - это экземпляры A (и A может быть даже одиночным). Существуют различные виды проверки, управления, отслеживания, аудита и т. Д., Которые могут быть реализованы в методах одного класса, которые создают экземпляры следующего.

Примером может быть операционная система, которая может создавать файловые системы, которые могут создавать каталоги, которые могут создавать файлы, но код приложения должен следовать этой цепочке. Например, даже если у него были руки на Каталоге, он не должен был бы создавать файл, дающий File.__init__ экземпляр Каталога, даже если это то, что будут делать каталоги при запросе создания файла.

Я ищу дизайнерское решение, подкрепленное некоторой реализацией, а не что-то защищенное пользователем - я понимаю, что тщетность последнего.

Единственное, что я думал так далеко:

  1. начать все имя класса, за исключением А с подчеркиванием
  2. _b доступа() только из случаев, _C() только из экземпляров B, и _D() только из экземпляров _c
  3. полагаются на прикладном уровне программистов соблюдать эту договоренность и непосредственно создавать экземпляры только (возможно одиночки) класса а

Модуль уровня «прятался» по исключение класса из списка __all__ модуля недостаточное, поскольку только влияет на конструкции import * - другой модуль все еще может достичь класса на import module, затем ссылаясь на module.class.

(Все это смутно напоминает проблемы C++, для которых требуется, чтобы два класса были друзьями, потому что они участвуют в двухсторонних отношениях: экземпляры каждого класса должны иметь возможность ссылаться на методы другого класса, которые управляют тем, что другая сторона отношения.)

Решение, которое может наилучшим образом соответствовать семантике и прагматике Python, заключается в определении D внутри C, C, определенных внутри B, и B, определенных внутри A. Это кажется ужасно уродливым способом втискивать несколько модулей стоимостью кода в один очень большой класс. (И представьте, если цепь опустилась еще на несколько уровней.) Это расширение более распространенных применений вложенных классов и, возможно, единственно технически обоснованного способа сделать это, но я никогда не видел такого рода структуру класса «русская кукла».

Идеи?

+0

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

+0

Это ужасно, но вместо того, чтобы пытаться сделать конструктор класса недоступным, вы можете проверить класс вызывающего метода во время построения, используя технику, аналогичную описанной здесь [http://stackoverflow.com/a/9812105/3895264]. Заметьте, я бы рекомендовал это, однако! – FujiApple

+0

Разделение - хорошая идея - извините, я не думал об этом. –

ответ

6

Python не очень хорошо полностью скрыть это объекты ... Даже если вы решили сделать «русскую куклу» закрытия, чтобы попытаться скрыть определение класса, пользователь все еще может получить на него ...

class A(object): 
    def create_b(self): 
     class B(object): 
      pass 
     return B() 

a = A() 
b = a.create_b() 
b_cls = type(b) 
another_b = b_cls() 

Проще говоря - если пользователь имеет доступ к исходному коду (и они могут получить его через inspect или, по крайней мере, они могут получить байткод через dis для тренированного глаза достаточно хорошо), то у них есть возможность создавать экземпляры вашего класса, если они достаточно мотивированы.

Как правило, это достаточно хорошо, чтобы просто документировать класс как нечто, что пользователь не должен создавать сами собой. Если они нарушают это доверие, это их вина, когда их программа вылетает и горит (и имеет некоторые неприятные побочные эффекты, эффект, который заставляет их загореться после очистки жесткого диска). Это симпатичная центральная философия питона, хорошо изложенная в python mail archives:

Ничто не является частным в python. Ни один экземпляр класса или класса не может держать вас подальше от всего, что внутри (это делает интроспекцию возможной и мощной). Python доверяет вам. В нем говорится: «Эй, если вы хотите, чтобы провалился в темных местах, я надеюсь, что у вас есть , и вы не беспокоитесь».

В конце концов, мы все согласны с взрослыми здесь.

C++ и Java не имеют такой философии (не в той же степени). Они позволяют создавать частные методы и статические элементы.

Perl-культура подобна python в этом отношении, но Perl выражает чувства немного по-другому. Как пишет книга «Верблюд»,

«Модуль Perl предпочел бы, чтобы вы остались из своей гостиной , потому что вас не пригласили не потому, что у него есть дробовик».

Но настроение идентично.

1

Просто из верхней части моей головы:

def B(object): 
    pass 

def D(object): 
    pass 

def bound(object): 
    if type(object) is C: 
     assert isinstance(D) 
    if type(object) is A: 
     assert isinstance(B) 
    else: 
     assert false 

@bound 
def C(D): 
    pass 

@bound 
def A(B): 
    pass 
+0

Этот код необходимо протестировать: в настоящее время он не запускается. Объяснение этой идеи также было бы очень полезно. – EOL