Учебный каталог Python
- Использование Python3 под Mac
- Типы данных для изучения Python
- Функция обучения Python
- Расширенные возможности обучения Python
- Функциональное программирование для изучения Python
- Модуль обучения Python
- Объектно-ориентированное программирование для изучения Python
- Расширенное объектно-ориентированное программирование для изучения Python
- Отладка ошибок и тестирование обучения Python
- Программирование ввода-вывода для изучения Python
- Процесс обучения Python и поток
- Регулярность изучения Python
- Общие модули для изучения Python
- Python обучение сетевому программированию
Инкапсуляция данных, наследование и полиморфизм — это всего лишь три основных понятия объектно-ориентированного программирования. В Python объектная ориентация также имеет множество дополнительных функций, таких как: множественное наследование, пользовательские классы, метаклассы и другие концепции.
_slots_
Роль: ограничить свойства экземпляра.
Python позволяет определить специальный класс при определении класса__slots__
переменная, чтобы ограничить свойства, которые может добавить экземпляр класса:
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
>>> s = Student() # 创建新的实例
>>> s.name = 'Michael' # 绑定属性'name'
>>> s.age = 25 # 绑定属性'age'
>>> s.score = 99 # 绑定属性'score'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'
так как'score'
не был размещен__slots__
, поэтому его нельзя связатьscore
собственность, пытаясь связатьscore
получитеAttributeError
ошибка.
использовать__slots__
обращать внимание,__slots__
Определенные свойства работают только с текущим экземпляром класса и не влияют на унаследованные подклассы:
>>> class GraduateStudent(Student):
... pass
...
>>> g = GraduateStudent()
>>> g.score = 9999
если также не определено в подклассах__slots__
, так что свойства, которые могут быть определены экземплярами подкласса, являются их собственными__slots__
плюс родительский класс__slots__
.
@property
Декораторы могут динамически добавлять функции к функциям. Для методов класса декораторы работают точно так же.@property
Декоратор отвечает за превращение метода в вызов свойства:
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
Чтобы превратить метод получения в свойство, просто добавьте@property
Вот и все, в это время@property
сам создает еще один декоратор@score.setter
, отвечающий за превращение метода установки в присваивание свойства, поэтому у нас есть управляемая операция свойства:
>>> s = Student()
>>> s.score = 60 # OK,实际转化为s.set_score(60)
>>> s.score # OK,实际转化为s.get_score()
60
>>> s.score = 9999
Traceback (most recent call last):
...
ValueError: score must between 0 ~ 100!
обратите внимание на это удивительное@property
, когда мы работаем со свойствами экземпляра, мы знаем, что свойства, скорее всего, не отображаются напрямую, а реализуются через методы получения и установки.
Вы также можете определить свойства только для чтения, определить только метод получения и не определять, что метод установки является свойством только для чтения:
class Student(object):
@property
def birth(self):
return self._birth
@birth.setter
def birth(self, value):
self._birth = value
@property
def age(self):
return 2015 - self._birth
вышеbirth
является свойством чтения-записи, иage
только одинтолько чтениесвойства, потому чтоage
может основываться наbirth
и текущее время.
множественное наследование
При проектировании отношения наследования классов обычно основная линия наследуется от одиночной линии, например,Ostrich
унаследовано отBird
. Однако, если вам нужно «подмешать» дополнительный функционал, вы можете сделать это через множественное наследование, например, пустьOstrich
Помимо наследования отBird
К тому же, а потом наследуют заодноRunnable
. Этот дизайн часто называют MixIn.
Чтобы лучше увидеть отношение наследования, положимRunnable
иFlyable
изменить наRunnableMixIn
иFlyableMixIn
. Точно так же вы можете определить хищниковCarnivorousMixIn
и травоядныеHerbivoresMixIn
, пусть у животного одновременно несколько MixIn:
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
pass
Цель MixIn — добавить несколько функций в класс Таким образом, при проектировании класса мы отдаем приоритет объединению функций нескольких MixIn посредством множественного наследования, а не разработке сложных многоуровневых отношений наследования.
пользовательский класс
к чему-то вроде
__slots__
эта форма__xxx__
Обратите внимание, что имена переменных или функций используются в Python для особых целей.__slots__
Мы уже умеем им пользоваться,__len__()
Мы также знаем, что метод заключается в том, чтобы позволить классу воздействовать наlen()
функция. Кроме того, в классах Python есть много таких специальных функций, которые могут помочь нам настроить классы.
_str_
>>> class Student(object):
... def __init__(self, name):
... self.name = name
... def __str__(self):
... return 'Student object (name: %s)' % self.name
...
>>> print(Student('Michael'))
Student object (name: Michael)
Распечатанный таким образом экземпляр не только красив, но и позволяет легко увидеть важные данные внутри экземпляра. Но вам не нужно вводить переменную напрямуюprint
, печатный пример по-прежнему выглядит не очень красиво:
>>> s = Student('Michael')
>>> s
<__main__.Student object at 0x109afb310>
Это связано с тем, что прямые вызовы отображаемых переменных не__str__()
, но__repr__()
, разница между ними__str__()
возвращает строку, которую видит пользователь, а__repr__()
Возвращает строку так, как ее видит разработчик программы, т. е.__repr__()
предназначен для отладки.
Решение состоит в том, чтобы определить другой__repr__()
. но обычно__str__()
и__repr__()
Код тот же, поэтому есть ленивый способ написания:
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student object (name=%s)' % self.name
__repr__ = __str__
_iter_
Если класс хочет использоваться дляfor ... in
Циклы, такие как список или кортеж, должны реализовывать__iter__()
метод, который возвращает итерируемый объект, а затем цикл for Python будет продолжать вызывать итеративный объект__next__()
метод для получения следующего значения в цикле, пока не встретитсяStopIteration
Выход из цикла при ошибке.
Давайте возьмем в качестве примера числовую последовательность Фибоначчи и напишем класс Фибоначчи, который может действовать в цикле for:
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1 # 初始化两个计数器a,b
def __iter__(self):
return self # 实例本身就是迭代对象,故返回自己
def __next__(self):
self.a, self.b = self.b, self.a + self.b # 计算下一个值
if self.a > 100000: # 退出循环的条件
raise StopIteration()
return self.a # 返回下一个值
Теперь попробуйте применить экземпляр Fib к циклу for:
>>> for n in Fib():
... print(n)
...
1
1
2
3
5
...
46368
75025
_getitem_
Чтобы извлечь элементы по индексу, как список, вам нужно реализовать__getitem__()
метод:
class Fib(object):
def __getitem__(self, n):
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
Теперь вы можете получить доступ к любому элементу последовательности по индексу:
>>> f = Fib()
>>> f[0]
1
>>> f[1]
1
>>> f[2]
2
>>> f[3]
3
>>> f[10]
89
>>> f[100]
573147844013817084101
Но у списка есть волшебный метод нарезки:
>>> list(range(100))[5:10]
[5, 6, 7, 8, 9]
Для Fib он сообщает об ошибке. Причина в том, что__getitem__()
Входящий параметр может быть объектом типа int или slice.slice
, так что судите:
class Fib(object):
def __getitem__(self, n):
if isinstance(n, int): # n是索引
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
if isinstance(n, slice): # n是切片
start = n.start
stop = n.stop
if start is None:
start = 0
a, b = 1, 1
L = []
for x in range(stop):
if x >= start:
L.append(a)
a, b = b, a + b
return L
_getattr_
Python имеет механизм для написания__getattr__()
метод, который динамически возвращает свойство:
class Student(object):
def __init__(self):
self.name = 'Michael'
def __getattr__(self, attr):
if attr=='score':
return 99
При вызове несуществующего свойства, напримерscore
, интерпретатор Python попытается вызвать__getattr__(self, 'score')
чтобы попытаться получить собственность, таким образом, у нас есть шанс вернутьсяscore
Значение:
>>> s = Student()
>>> s.name
'Michael'
>>> s.score
99
Также совершенно нормально возвращать функцию:
class Student(object):
def __getattr__(self, attr):
if attr=='age':
return lambda: 25
Просто позвоните, чтобы измениться на:
>>> s.age()
25
Обратите внимание, что он вызывается только в том случае, если свойство не найдено.__getattr__
, существующие свойства, такие какname
, не будет в__getattr__
Найти в.
Также обратите внимание, что произвольные вызовы, такие какs.abc
вернусьNone
, это потому, что мы определяем__getattr__
Возврат по умолчаниюNone
. Чтобы класс реагировал только на несколько определенных свойств, мы должны следовать соглашению и бросатьAttributeError
ошибка:
class Student(object):
def __getattr__(self, attr):
if attr=='age':
return lambda: 25
raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)
_call_
Экземпляр объекта может иметь свои собственные свойства и методы, когда мы вызываем методы экземпляра, мы используемinstance.method()
звонить.
Точно так же любой класс должен только определить__call__()
метод, вы можете вызвать экземпляр напрямую. См. пример:
class Student(object):
def __init__(self, name):
self.name = name
def __call__(self):
print('My name is %s.' % self.name)
>>> s = Student('Michael')
>>> s() # self参数不要传入
My name is Michael.
__call__()
Параметры также могут быть определены. Прямой вызов экземпляра подобен вызову функции, поэтому вы можете думать об объекте как о функции, а о функции как об объекте, потому что между ними нет принципиальной разницы.
Если вы думаете об объектах как о функциях, то сами функции могут быть динамически созданы во время выполнения, потому что экземпляры классов создаются во время выполнения, поэтому мы стираем грань между объектами и функциями.
Итак, как определить, является ли переменная объектом или функцией? На самом деле чаще нам нужно судить о том, можно ли назвать объект, а объект, который можно назвать, являетсяCallable
Объекты, такие как функции и те, которые мы определили выше с помощью__call__()
экземпляр класса:
>>> callable(Student())
True
>>> callable(max)
True
>>> callable([1, 2, 3])
False
>>> callable(None)
False
>>> callable('str')
False
перечисляемый класс
"класс перечисления"Enum
Является ли определение типа класса для типа перечисления, тогда каждая константа является уникальным экземпляром класса.
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
Итак, мы получаемMonth
Класс перечисления типа, который можно использовать напрямуюMonth.Jan
для ссылки на константу или для перечисления всех ее членов:
for name, member in Month.__members__.items():
print(name, '=>', member, ',', member.value)
value
Атрибуты автоматически назначаются членамint
постоянная, по умолчанию от1
Начните считать.
Если вам нужен более точный контроль над типом перечисления, вы можете начать сEnum
Получите пользовательский класс:
from enum import Enum, unique
@unique
class Weekday(Enum):
Sun = 0 # Sun的value被设定为0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6
@unique
Декораторы могут помочь нам убедиться, что нет повторяющихся значений.
Есть несколько способов получить доступ к этим типам перечислений:
>>> day1 = Weekday.Mon
>>> print(day1)
Weekday.Mon
>>> print(Weekday.Tue)
Weekday.Tue
>>> print(Weekday['Tue'])
Weekday.Tue
>>> print(Weekday.Tue.value)
2
>>> print(day1 == Weekday.Mon)
True
>>> print(day1 == Weekday.Tue)
False
>>> print(Weekday(1))
Weekday.Mon
>>> print(day1 == Weekday(1))
True
>>> Weekday(7)
Traceback (most recent call last):
...
ValueError: 7 is not a valid Weekday
>>> for name, member in Weekday.__members__.items():
... print(name, '=>', member)
...
Sun => Weekday.Sun
Mon => Weekday.Mon
Tue => Weekday.Tue
Wed => Weekday.Wed
Thu => Weekday.Thu
Fri => Weekday.Fri
Sat => Weekday.Sat
Видно, что на константы перечисления можно ссылаться по именам членов, а константы перечисления можно получить напрямую по значению value.
метакласс
type()
Самая большая разница между динамическими языками и статическими языками заключается в том, что определения функций и классов не определяются во время компиляции, а динамически создаются во время выполнения.
Допустим, мы хотим определитьHello
класс, просто напишиhello.py
Модуль:
class Hello(object):
def hello(self, name='world'):
print('Hello, %s.' % name)
Когда интерпретатор Python загружаетсяhello
модуль, все операторы модуля будут выполняться по очереди, а результатом выполнения является динамическое созданиеHello
объект класса, тест выглядит следующим образом:
>>> from hello import Hello
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class 'hello.Hello'>
type()
Функция может смотреть на тип типа или переменной,Hello
это класс, тип которогоtype
,иh
является экземпляром, тип которого является классомHello
.
Мы говорим, что определение класса создается динамически во время выполнения, и способ создания класса заключается в использованииtype()
функция.
type()
Функции могут как возвращать тип объекта, так и создавать новые типы, например, мы можем передатьtype()
функция созданаHello
класс без прохожденияclass Hello(object)...
Определение:
>>> def fn(self, name='world'): # 先定义函数
... print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class '__main__.Hello'>
Чтобы создать объект класса,type()
Функция передает по очереди 3 параметра:
- название класса;
- Коллекция унаследованных родительских классов.Обратите внимание, что Python поддерживает множественное наследование.Если есть только один родительский класс, не забудьте написать один элемент кортежа;
- Имя метода класса привязано к функции, здесь мы помещаем функцию
fn
привязать к имени методаhello
начальство.
пройти черезtype()
Класс, созданный функцией, точно такой же, как и запись класса напрямую, потому что, когда интерпретатор Python встречает определение класса, он просто просматривает синтаксис определения класса, а затем вызываетtype()
Функции создают классы.
Обычно мы используемclass Xxx...
однако для определения классаtype()
Функции также позволяют нам динамически создавать классы, то есть сами динамические языки поддерживают динамическое создание классов во время выполнения, что сильно отличается от статических языков.Чтобы создавать классы во время выполнения на статических языках, вы должны создавать строки исходного кода. а затем вызов компилятора или создание реализаций байт-кода с помощью некоторых инструментов, по сути, является динамической компиляцией, которая может быть очень сложной.
metaclass
Помимо использованияtype()
Помимо динамического создания классов, для управления поведением при создании классов вы также можете использовать метаклассы.
метакласс, дословно переводится как метакласс, простое объяснение:
Когда мы определяем класс, мы можем создать экземпляр на основе этого класса, поэтому: сначала определите класс, а затем создайте экземпляр.
Но что, если мы хотим создать классы? Затем класс должен быть создан в соответствии с метаклассом, поэтому: сначала определите метакласс, а затем создайте класс.
Связь такова: сначала определите метакласс, вы можете создать класс и, наконец, создать экземпляр.
Итак, метакласс позволяет вам создавать классы или изменять классы. Другими словами, вы можете думать о классах как об «экземплярах», созданных метаклассами.