Искажение имени питона


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

верно ли то же самое для Python? Должен ли я сначала использовать два ведущих подчеркивания на все, и только сделать их менее скрытыми (только одно подчеркивание), поскольку они мне нужны?

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

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

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

9 67

9 ответов:

если вы сомневаетесь, оставьте его "общественность", я имею в виду, не добавляйте ничего, чтобы скрыть имя атрибута. Если у вас есть класс с некоторым внутренним значением, не беспокойтесь об этом. Вместо того, чтобы писать:

class Stack(object):

    def __init__(self):
        self.__storage = [] # Too uptight

    def push(self, value):
        self.__storage.append(value)

написать это по умолчанию:

class Stack(object):

    def __init__(self):
        self.storage = [] # No mangling

    def push(self, value):
        self.storage.append(value)

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

если вы действительно хотите отправить сообщение своим пользователям, сказав: "Не могу прикоснуться к этому!", обычный способ-это предшествовать переменной с один подчеркивания. Это просто условность, но люди это понимают и проявляют двойную осторожность при работе с такими вещами:

class Stack(object):

    def __init__(self):
        self._storage = [] # This is ok but pythonistas use to be relaxed about it

    def push(self, value):
        self._storage.append(value)

это также может быть полезно для предотвращения конфликта между именами свойств и именами атрибутов:

 class Person(object):
     def __init__(self, name, age):
         self.name = name
         self._age = age if age >= 0 else 0

     @property
     def age(self):
         return self._age

     @age.setter
     def age(self, age):
         if age >= 0:
             self._age = age
         else:
             self._age  = 0

как насчет двойника подчеркивание? Ну, магия двойного подчеркивания используется в основном чтобы избежать случайной перегрузки методов и конфликтов имен с атрибутами суперклассов. Это может быть очень полезно, если вы пишете класс, который, как ожидается, будет расширен много раз.

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

EDIT: почему это так? Ну, обычный стиль Python не подчеркивает, что делает вещи частными - наоборот! Причин тому много-большинство из них спорные... Давайте посмотрим на некоторые из них.

Python имеет свойства

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

однако, это не так просто. Для например, классы Java имеют много атрибутов и геттеры, что просто get значения и сеттеры, что просто set значения. Вам нужно, скажем, семь строк кода, чтобы объявить один атрибут - который программист Python сказал бы, что это излишне сложно. Кроме того, на практике вы просто пишете этот код, чтобы получить одно открытое поле, так как вы можете изменить его значение с помощью геттеров и сеттеров.

так зачем следовать это частная политика по умолчанию? Просто сделайте ваши атрибуты общедоступными по умолчанию. Конечно, это проблематично в Java, потому что если вы решите добавить некоторую проверку к своему атрибуту, это потребует от вас изменить все

person.age = age;

в вашем коде, скажем,

person.setAge(age);

будучи setAge():

public void setAge(int age) {
    if (age >= 0) {
        this.age = age;
    } else {
        this.age = 0;
    }
}

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

однако вам не нужно делать это в Python, так как Python имеет свойства. Если бы у вас был этот класс:

 class Person(object):
     def __init__(self, name, age):
         self.name = name
         self.age = age

и тогда вы решите проверить возраст, вам не нужно менять person.age = age куски кода. Просто добавьте свойство (как показано ниже)

 class Person(object):
     def __init__(self, name, age):
         self.name = name
         self._age = age if age >= 0 else 0

     @property
     def age(self):
         return self._age

     @age.setter
     def age(self, age):
         if age >= 0:
             self._age = age
         else:
             self._age  = 0

если вы можете сделать это и по-прежнему использовать person.age = age, зачем вы добавляете частные поля и геттеры и сеттеры?

(Также см. Python-это не Java и эта статья о вреде использования геттеров и сеттеров.).

все видно в любом случае-и попытка скрыть просто усложняет вашу работу

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

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

проблема не в том, чтобы быть возможным видеть-это быть требуются посмотреть

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

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

так сказал Гвидо

Ну, это не спорный: он так и сказал, на самом деле. (Ищите " открытое кимоно.")

это культура

Да, есть некоторые причины, но нет причин для убийства. Это в основном культурный аспект программирования на Python. Честно говоря, это может быть и по - другому, но это не так. Кроме того, вы можете просто спросить наоборот: почему некоторые языки используют частные атрибуты по умолчанию? По той же самой основной причине для практики Python: потому что это культура этих языков, и каждый выбор имеет свои преимущества и недостатки.

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

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

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

моя стратегия в Python это:

  1. просто напишите эту чертову вещь, не делайте никаких предположений о том, как ваши данные должны быть защищены. Это предполагает, что вы пишете, чтобы создать идеальные интерфейсы для ваших проблем.
  2. используйте начальное подчеркивание для вещей, которые наверное не будет использоваться внешне и не является частью обычного " клиентского кода" взаимодействие.
  3. использовать двойное подчеркивание только для вещей, которые являются чисто удобство внутри класса, или причинит значительный ущерб, если случайно.

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

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

во - первых- что такое коверканье имени?

имя искажения вызывается, когда вы находитесь в определении класса и использовать __any_name или __any_name_, то есть два (или более) начальные подчеркивания и не более одного конечного подчеркивания.

class Demo:
    __any_name = "__any_name"
    __any_other_name_ = "__any_other_name_"

и так:

>>> [n for n in dir(Demo) if 'any' in n]
['_Demo__any_name', '_Demo__any_other_name_']
>>> Demo._Demo__any_name
'__any_name'
>>> Demo._Demo__any_other_name_
'__any_other_name_'

когда сомневаешься, что делать?

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

A потенциальное значение заключается в том, чтобы избежать конфликтов имен с подклассами, которые хотят переопределить поведение, чтобы функциональность родительского класса продолжала работать должным образом. Тем не менее,пример в документации Python не является Лисков заменяемым, и никакие примеры не приходят на ум, где я нашел это полезным.

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

мой личный подход заключается в том, чтобы намеренно избегать его. Я работаю на очень большой кодовой базе. Редкое использование его торчит как больной палец и не кажется оправданным.

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

PEP 8

PEP 8, руководство по стилю стандартной библиотеки Python, в настоящее время говорит (сокращенно):

существуют некоторые разногласия по поводу использования __names.

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

  1. обратите внимание, что в искаженном имени используется только простое имя класса, поэтому если подкласс выбирает одно и то же имя класса и имя атрибута, вы все еще можете получить имя столкновения.

  2. имя искажение может сделать определенные виды использования, такие как отладка и __getattr__() , менее удобно. Однако алгоритм искажения имени хорошо документирован и легко выполняется вручную.

  3. не все любят имя калечить. Постарайтесь сбалансировать необходимость избегать случайных столкновений имен с потенциальным использованием продвинутыми абонентами.

как это работает?

если вы добавляете два подчеркивания (без окончания двойных подчеркиваний) в определении класса имя будет искажено, и подчеркивание, за которым следует имя класса, будет добавлено к объекту:

>>> class Foo(object):
...     __foobar = None
...     _foobaz = None
...     __fooquux__ = None
... 
>>> [name for name in dir(Foo) if 'foo' in name]
['_Foo__foobar', '__fooquux__', '_foobaz']

обратите внимание, что имена будут искажены только при анализе определения класса:

>>> Foo.__test = None
>>> Foo.__test
>>> Foo._Foo__test
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'Foo' has no attribute '_Foo__test'

кроме того, новички в Python иногда испытывают проблемы с пониманием того, что происходит, когда они не могут вручную получить доступ к имени, которое они видят в определении класса. Это не является веской причиной против этого, но это то, что нужно учитывать, если у вас есть обучающаяся аудитория.

Один Символ Подчеркивания?

если конвенция должна использовать только одно подчеркивание, я также хотел бы знать обоснование.

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

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

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

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

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

тут уже много хороших ответов, но я хочу предложить еще один. Это также частично ответ людям, которые продолжают говорить, что двойное подчеркивание не является частным (это действительно так).

Если вы посмотрите на Java/C#, оба они имеют private/protected / public. Все это -во время компиляции конструкции. Они применяются только во время компиляции. Если бы вы использовали отражение в Java / C#, вы могли бы легко получить доступ к private метод.

теперь каждый раз, когда вы вызываете функцию в Python, вы по своей сути используете отражение. Эти части кода одинаковы в Python.

lst = []
lst.append(1)
getattr(lst, 'append')(1)

синтаксис "точки" - это только синтаксический сахар для последнего фрагмента кода. В основном потому, что использование getattr уже уродливо только с одним вызовом функции. Дальше будет только хуже.

Так что, есть не могу быть Java / C# версия private, так как Python не компилирует код. Java и C# не удается проверить, является ли функция частной или общедоступной во время выполнения, поскольку эта информация отсутствует (и она не знает, откуда вызывается функция).

теперь с этой информацией имя искажения двойного подчеркивания имеет наибольший смысл для достижения "приватности". Теперь, когда функция вызывается из экземпляра 'self' и он замечает, что он начинается с'__', он просто выполняет имя mangling прямо там. Это просто синтаксический сахар. Что синтаксический сахар позволяет эквивалент 'private' на языке, который использует отражение только для доступа к элементам данных.

отказ от ответственности: я никогда не слышал, чтобы кто-нибудь из разработки Python говорил что-то подобное. Настоящая причина отсутствия "частного" - это культура, но вы также заметите, что большинство скриптовых/интерпретируемых языков не имеют частного. Строго принудительное частное не является практичным ни в чем, кроме времени компиляции.

во-первых: почему вы хотите скрыть свои данные? Почему это так важно?

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

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

вот как мы живем, и мы ну и ладно.

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

выбранный ответ хорошо объясняет, как свойства устраняют необходимость частная атрибутами, но я бы также добавил, что функции на уровне модуля устраняют необходимость частные методы.

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

на первый взгляд он должен быть таким же, как и для других языков (под "другим" я имею в виду Java или C++), но это не так.

в Java вы сделали закрытыми все переменные, которые не должны быть доступны снаружи. В то же время в Python вы не можете этого достичь, так как нет "приватности" (как говорит один из принципов Python - "мы все взрослые"). Поэтому двойное подчеркивание означает только "ребята, не используйте это поле напрямую". То же самое значение имеет подчеркивание паленого, которое в то же время не имеет вызовите любую головную боль, когда вам нужно наследовать от рассматриваемого класса (просто пример возможной проблемы, вызванной двойным подчеркиванием).

Итак, я бы рекомендовал вам использовать один символ подчеркивания по умолчанию для "частных" членов.

следующий фрагмент кода объяснит все различные случаи:

  • два ведущих подчеркивания (__a)
  • одно начальное подчеркивание (_a)
  • без подчеркивания (a)

    class Test:
    
    def __init__(self):
        self.__a = 'test1'
        self._a = 'test2'
        self.a = 'test3'
    
    def change_value(self,value):
        self.__a = value
        return self.__a
    

печать всех допустимых атрибутов тестового объекта

testObj1 = Test()
valid_attributes = dir(testObj1)
print valid_attributes

['_Test__a', '__doc__', '__init__', '__module__', '_a', 'a', 
'change_value']

здесь вы можете видеть, что имя __a было изменено на _Test__a, чтобы предотвратить переопределение этой переменной любым из подклассов. Это понятие известно как " имя Калечить " в python. Вы можете получить доступ к этому следующим образом:

testObj2 = Test()
print testObj2._Test__a

test1

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

testObj3 = Test()
print testObj3._a

test2

переменная может быть доступ из любого места это как переменная открытого класса.

testObj4 = Test()
print testObj4.a

test3

надеюсь, что ответ помог вам :)