Частный конструктор в Python - PullRequest
56 голосов
/ 21 ноября 2011

Как создать приватный конструктор, который должен вызываться только статической функцией класса, а не откуда-либо еще?

Ответы [ 6 ]

28 голосов
/ 21 ноября 2011

Как мне создать приватный конструктор?

По сути, невозможно , так как python не использует конструкторы так, как вы думаете, если вы пришли из других языков ООП, и потому что python не обеспечивает соблюдение конфиденциальности , это просто имеет определенный синтаксис, позволяющий предположить, что данный метод / свойство следует считать закрытым. Позвольте мне уточнить ...

Первое: ближайший к конструктору, который вы можете найти в python, - это метод __new__ , но он очень редко используется (обычно вы используете __init__, который модифицирует только что созданный объект ( фактически он уже имеет self в качестве первого параметра).

Несмотря на это, python основан на предположении, что каждый является согласным взрослым , поэтому приватный / публичный не применяется, как некоторые другие языки.

Как уже упоминалось некоторыми другими респондентами, методы, которые должны быть «приватными», обычно начинаются с одного или двух символов подчеркивания: _private или __private. Разница между ними заключается в том, что последний скремблирует имя метода, поэтому вы не сможете вызвать его извне экземпляра объекта, а первый - нет.

Например, если ваш класс A определяет и _private(self), и __private(self):

>>> a = A()
>>> a._private()   # will work
>>> a.__private()  # will raise an exception

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

НТН!

16 голосов
/ 28 сентября 2017

Префиксы _ и __ не предлагают решения, ограничивающего создание экземпляра объекта определенной «фабрикой», однако Python является мощным набором инструментов, и желаемое поведение может быть достигнуто несколькими способами (как продемонстрировал @Jesse W at Z).Вот возможное решение, которое делает класс общедоступным (допускает isinstance и т. Д.), Но гарантирует, что построение возможно только методами класса:

class OnlyCreatable(object):

    __create_key = object()

    @classmethod
    def create(cls, value):
        return OnlyCreatable(cls.__create_key, value)

    def __init__(self, create_key, value):
        assert(create_key == OnlyCreatable.__create_key), \
            "OnlyCreatable objects must be created using OnlyCreatable.create"
        self.value = value

Построение объекта с помощью метода класса create:

>>> OnlyCreatable.create("I'm a test") 
<__main__.OnlyCreatable object at 0x1023a6f60>

При попытке создать объект без использования create создание метода класса завершается неудачно из-за утверждения:

>>> OnlyCreatable(0, "I'm a test")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 11, in __init__
AssertionError: OnlyCreatable objects can only be created using OnlyCreatable.create

При попытке создать объект путем имитацииcreate Создание метода класса завершается неудачно из-за искажения компилятором OnlyCreatable.__createKey.

>>> OnlyCreatable(OnlyCreatable.__createKey, "I'm a test")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'OnlyCreatable' has no attribute '__createKey'

Единственный способ создать OnlyCreatable вне метода класса - это узнать значение OnlyCreatable.__create_key.Поскольку значение этого атрибута класса генерируется во время выполнения, а его имя имеет префикс __, помечающий его как недоступный, фактически «невозможно» получить это значение и / или построить объект.

9 голосов
/ 25 сентября 2014

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

#Define the class within the factory method
def factory():
  class Foo:
    pass
  return Foo()

ИЛИ

#Assign the class as an attribute of the factory method
def factory():
  return factory.Foo()
class Foo:
  pass
factory.Foo = Foo
del Foo

(Примечание: Это по-прежнему позволяет ссылаться на класс извне (например, для проверок isinstance), но совершенно очевидно, что вы не должны создавать его экземпляры напрямую.)

ИЛИ

#Assign the class to a local variable of an outer function
class Foo:
  pass
def factory_maker():
  inner_Foo=Foo
  def factory():
    return inner_Foo()
  return factory
factory = factory_maker()
del Foo
del factory_maker

Это делает невозможным (по крайней мере, без использования хотя бы одного магического свойства (двойное подчеркивание)) для доступа к классу Foo, но все же позволяет нескольким функциям использовать его (определив их перед удалением глобального имени Foo.

7 голосов
/ 21 ноября 2011

Цитирование Руководство по стилю Python (PEP 8) :

Кроме того, распознаются следующие специальные формы с использованием начальных или конечных подчеркиваний (их обычно можно комбинировать с любымиусловное обозначение):

  • _single_leading_underscore: слабый индикатор "внутреннего использования".Например, «from M import *» не импортирует объекты, имя которых начинается с подчеркивания.

  • single_trailing_underscore_: используется по соглашению, чтобы избежать конфликтов с ключевым словом Python, например, Tkinter.Toplevel(master, class_='ClassName')

  • __double_leading_underscore: при присвоении имени атрибуту класса вызывается искажение имени (внутри класса FooBar __boo становится _FooBar__boo; см. Ниже).

  • __double_leading_and_trailing_underscore__: «магические» объекты или атрибуты, которые живут в управляемых пользователем пространствах имен.Например, __init__, __import__ или __file__.Никогда не изобретайте такие имена;используйте их только в соответствии с документацией.

3 голосов
/ 21 ноября 2011

Прежде всего, термин «конструктор» не применяется к Python, потому что, хотя метод __init__() играет роль единицы, это просто метод, который вызывается, когда объект уже создан и требует инициализации.

Каждый метод класса в Python является открытым.Обычно программисты помечают «частные» методы как _ или __ в названии метода, например:

# inheriting from object is relevant for Python 2.x only
class MyClass(object): 
    # kinda "constructor"
    def __init__(self):
        pass

    # here is a "private" method
    def _some_method(self):
        pass

    # ... and a public one
    def another_method(self):
        pass
1 голос
/ 21 июня 2017
class MongoConn(object):
    @classmethod
    def instance(cls):
        if not hasattr(cls, '_instance'):
            cls._instance = cls()
        return cls._instance

    def __init__(self):
        assert not hasattr(self.__class__, '_instance'), 'Do not call constructor directly!'

Если вы хотите один экземпляр.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...