Эта статья возникла из личного публичного аккаунта:TechFlow, оригинальность это не просто, прошу внимания
СегодняЧасть 11 тем PythonВ этой статье давайте поговорим о некоторых продвинутых способах использования объектно-ориентированного подхода.
__slots__
Если вы посмотрите на код некоторых больших коров на github, вы обнаружите, что многие большие коровы часто добавляют ключевое слово __slots__ в начало класса. Если вам достаточно любопытно, вы можете попробовать удалить это ключевое слово и повторить попытку, и вы обнаружите, что после его удаления ничего не происходит, все по-прежнему работает нормально.
Итак, что именно делает это ключевое слово __slots__?
В основном он имеет две функции, давайте сначала поговорим о первой функции, котораяОграничить использование пользователей.
Все мы знаем, что Python — очень гибкийдинамический язык, многие вещи, которые кажутся совершенно невыносимыми в других языках, осуществимы в Python, что также является философией дизайна Python, жертвуя эффективностью ради гибкости и удобства кода. Например, давайте рассмотрим очень простой пример: поскольку Python — динамический язык, члены класса могут быть созданы динамически даже после создания класса. Это абсолютно невозможно в статическом языке, мы можем вызывать только существующие свойства в классе, даневозможно или сложноДобавьте новые свойства.
Например, этот код:
class Exp:
def __init__(self):
self.a = None
self.b = None
if __name__ == "__main__":
exp = Exp()
exp.c = 3
print(exp.c)
Мы определяем класс с именем Exp и создаем для него два члена a и b. Но когда мы используемПрисвоение производится члену c. Вы должны знать, что в классе Exp нет члена c, но программа не сообщит об ошибке После того, как мы запустим ее таким образом, она добавит c в экземпляр.
С одной стороны, это, конечно, очень гибко, но, с другой стороны, это также оставляетскрытая опасность. Если пользователи произвольно добавляют атрибуты, это может вызвать неизвестные проблемы, особенно в сложных системах. Поэтому иногда ради строгости мы не хотим, чтобы пользователи вносили такие динамические изменения. __slots__ используется именно для этого.
Мы добавляем это ключевое слово, и результат будет другим, когда мы запустим его снова:
class Exp:
__slots__ = ['a', 'b']
def __init__(self):
self.a = None
self.b = None
if __name__ == "__main__":
exp = Exp()
exp.c = 3
print(exp.c)
Если вы запустите этот код, вы получите ошибку,Подскажите, что в объекте Exp нет члена c, то есть мы можем использовать только элементы, определенные в ключевом слове __slots__, а элементы, которые не определены, не могут быть созданы по желанию, что ограничивает использование пользователей.
Хотя сейчас большинство людей используют это ключевое слово для этой цели, к сожалению, изначальное намерение создателя Python не в этом. Это говорит о второй роли ключевого слова __slots__, котораясохранить память.
Если вы понимаете основные принципы реализации Python, вы обнаружите, что в PythonСловарь создается для каждого экземпляра, — это знаменитый словарь __dict__. Именно потому, что за этим стоит словарь, мы можем создавать члены, которые не существуют изначально, и поддерживать такие динамические эффекты. Мы можем вручную вызвать этот словарь для вывода его содержимого. Прежде чем мы добавим ключевое слово __slots__, вывод будет следующим:
{'a': None, 'b': None}
Но после добавления этого ключевого слова он будетполучить ошибку, сообщит вам, что в объекте Exp нет члена __dict__. Причина очень проста, потому что использование dict для обслуживания экземпляра будет потреблять много памяти и хранить много дополнительных данных.После использования __slots__ Python больше не будет создавать словарь для обслуживания экземпляра, а будет использовать фиксированный размер .массив, который экономит много места. Эта экономия не маленькая, вообще можетСэкономьте больше половины. То есть приносится в жертву определенная гибкость и гарантируется производительность. Это также первоначальное намерение ключевого слова __slots__, но теперь многие люди используют его не в том месте.
property
Это ключевое слово было упомянуто в статье, но меня очень смущает, что из-за ограниченного понимания его, когда я писал статью ранее, в некоторых объяснениях есть некоторые ошибки, поэтому я упомяну здесь использование этого ключевого слова в качестве компенсации. .
недвижимость может нам помочьПрисвоение и получение некоторых свойств в классе привязки, то есть получить и установить. Давайте посмотрим на пример:
class Exp:
def __init__(self, param):
self.param = param
@property
def param(self):
return self._param
@param.setter
def param(self, value):
self._param = value
Аннотация свойства здесь будет выполняться, когда мы вызываем .param, а param.setter будет выполняться, когда мы присваиваем значение свойству param. Итак, вам может быть интересно, почему мы используем self.param = param вместо self._param = param, когда мы инициализируем в методе __init__, потому что, когда мы выполняем первый, PythonТо же самое вызовет @param.setterЭто аннотация, поэтому нам не нужно писать последнюю форму. Конечно, вы можете написать это, но они полностью эквивалентны.
Как бывший программист JavaДобавьте методы get и set ко всем переменным в классе.Это почти политкорректно, поэтому мне особенно нравится добавлять свойства ко всем свойствам в классе. ноэто неправильно, plus свойство очень трудоемкое, так что не делайте этого без необходимости, мы просто вызываем его напрямую для присваивания, при необходимости, мы можем вручную написать методы get и set. Итак, вопрос в том, что, поскольку это не спецификация, почему мы используем свойство?
Ответ прост, ибоПроверить тип переменной.
Поскольку Python является динамическим языком и имеет неявную типизацию, мы не знаем, какой это тип переменной, когда мы ее получаем, и какой тип ей назначает пользователь. Поэтому в некоторых случаях мы можем захотеть ограничить и сообщить пользователю, что эта переменная может быть присвоена только этому типу, иначе будет сообщено об ошибке. Используя свойство, мы можем легко это сделать.
class Exp:
def __init__(self, param):
self.param = param
@property
def param(self):
return self._param
@param.setter
def param(self, value):
if not isinstance(value, str):
raise TypeError('Want a string')
self._param = value
Кроме того, другое использование собственностиЗамещающая функция. Например:
class Exp:
def __init__(self, param):
self.param = param
@property
def param(self):
return self._param
@param.setter
def param(self, value):
if not isinstance(value, str):
raise TypeError('Want a string')
self._param = value
@property
def hello(self):
return 'hello ' + self.param
Таким образом, мы можем использовать .hello вместо вызова функции, которая на самом деле является своего родаДинамический расчет. Результат hello не сохраняется и будет выполнен позже, когда мы его вызовем, что очень удобно в некоторых сценариях.
Соглашения об именах
Наконец, давайте взглянем на соглашения об именах в объектах Python.В предыдущей статье мы говорили, что в Python нет различий между открытыми и закрытыми полями.Все поля открыты, что означает, что пользователь может получить все поля и методы класса. Чтобы стандартизировать, программисты договариваются и решают всеПодчеркнутые методы и переменные считаются закрытымиДа, даже если мы можем это назвать, но в целом мы этого не делаем.
Поэтому обычно мы пишем два метода: один — открытый интерфейс, а другой — внутренняя реализация. Когда мы вызываем, мы вызываем только общедоступный интерфейс, а общедоступный интерфейс вызывает внутреннюю реализацию. Это стало соглашением в Python, потому что, когда мы вызываем внутренние методы, мы часто передаем некоторые внутренние параметры.
Давайте рассмотрим простой пример:
class ExpA:
def __init__(self):
pass
def public_func(self):
self._private_func()
def _private_func(self):
print('private ExpA')
if __name__ == "__main__":
exp = ExpA()
exp.public_func()
В дополнение к _ мы часто видим некоторыеПеременные и методы с двумя символами подчеркивания, тогда какая между ними разница?
Чтобы ответить на этот вопрос, давайте рассмотрим следующий пример:
class ExpA:
def __init__(self):
pass
def public_func(self):
self.__private_func()
def __private_func(self):
print('private ExpA')
class ExpB(ExpA):
def __init__(self):
pass
def public_func(self):
self.__private_func()
def __private_func(self):
print('private ExpB')
if __name__ == "__main__":
exp = ExpB()
exp.public_func()
exp._ExpB__private_func()
exp._ExpA__private_func()
Каким будет конечный результат?
Давайте попробуем и узнаем, что вывод первой строки — это приватный ExpB, что не проблема. Но каковы последние два?
Последние два — __private_func, ноСистема автоматически переименовывает его. Причина переименования также проста, потому что PythonМетод с двумя символами подчеркивания запрещено переопределять подклассами. Таким образом, разница между ними заключается в том, что они оба считаются закрытыми методами и свойствами, но одно подчеркивание позволяет переопределять подклассы, а два подчеркивания - нет. Так что если мы надеемся, что один из наших методов не будет переопределен подклассами во время разработки, то нам нужно добавить два символа подчеркивания.
Наконец, давайте рассмотрим небольшую проблему. В C++, когда имена наших переменных конфликтуют с системными ключевыми словами, мы часто добавляем _ перед переменной, чтобы отличить ее. Но поскольку в Python символам подчеркивания присваиваются значения, мы не можем этого сделать, так что же нам делать, когда переменные конфликтуют? Ответ также очень прост, мы можемдобавить подчеркивание после, например лямбда_.
Суммировать
Просмотрите сегодняшний контент, в основном использование __slots__, свойства и подчеркивания в классах. Все три являются объектно-ориентированными в Python.часто используетсяЗнание о них не только позволит нам писать более стандартизированный код, но и поможет нам понять исходный код других больших коров, так что это очень необходимо.
На сегодняшней статье все. Если вы чувствуете, что что-то приобрели, пожалуйста, нажмитеПодпишитесь или сделайте ретвитЧто ж, твое маленькое усилие много значит для меня.