python 面向对象
Prerequisite
如题所示,面向对象
参考文章:廖雪峰
基础面向对象
访问限制
- 实例的变量名如果以
__
开头,就变成了一个私有变量,只有内部可以访问 - 如果以一个下划线开头的实例变量名,比如
_name
,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问” - 类似
__xxx__
的属性和方法在 Python 中都是有特殊用途的,比如__len__
方法返回长度,如果你调用len()
函数试图获取一个对象的长度,实际上,在len()
函数内部,它自动去调用该对象的__len__()
方法
# 所以,下面的代码是等价的
len('ABC')
# 3
'ABC'.__len__()
# 3
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
student = Student("tuan", "666")
# 报错,访问不了私有变量
print(student.__name)
print(student.__score)
student.print_score()
# tuan: 666
如果突然运行外部获取私有变量,可以设置 get_name
函数
如果要更改里面的私有变量,不妨设置 set_name
函数
def get_name(self):
return self.__name
def set_score(self, score):
if 0 <= score <= 100:
self.__score = score
else:
raise ValueError('bad score')
鸭子类型
简单的说,就是无论是不是源自同一个父类或者祖父类等,只有它们有相同的函数,那么就是鸭子类型
class Animal(object): #编写Animal类
def run(self):
print("Animal is running...")
class Dog(Animal): #Dog类继承Amimal类,没有run方法
pass
class Cat(Animal): #Cat类继承Animal类,有自己的run方法
def run(self):
print('Cat is running...')
pass
class Car(object): #Car类不继承,有自己的run方法
def run(self):
print('Car is running...')
class Stone(object): #Stone类不继承,也没有run方法
pass
def run_twice(animal):
animal.run()
animal.run()
print('\n')
run_twice(Animal())
run_twice(Dog())
run_twice(Cat())
run_twice(Car())
run_twice(Stone())
"""
Animal is running...
Animal is running...
Animal is running...
Animal is running...
Cat is running...
Cat is running...
Car is running...
Car is running...
# Stone 类就报错了,因为他没有 run 函数
Traceback (most recent call last):
File "C:\Users\WPS\Desktop\Temporary\test.py", line 29, in <module>
run_twice(Stone())
File "C:\Users\WPS\Desktop\Temporary\test.py", line 21, in run_twice
animal.run()
AttributeError: 'Stone' object has no attribute 'run'
"""
实例属性和类属性
实例属性大于类属性,直接举例
class Student(object):
name = 'Bob'
# 类属性
student = Student()
print(student.name)
# Bob
# 添加实例属性
student.name = 'Alice'
print(student.name)
# Alice
# 删除实例属性
del student.name
print(student.name)
# Bob
高级面向对象
限制实例属性使用 slots
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
s = Student() # 创建新的实例
s.name = 'Michael' # 绑定属性'name'
s.age = 25 # 绑定属性'age'
s.score = 99 # 绑定属性'score'
# 失败,因为 score 没有被放到__slots__中
class GraduateStudent(Student):
pass
g = GraduateStudent()
g.score = 9999
# 成功,因为__slots__对继承的子类是不起作用的
# 但是如果子类也使用__slots__,那么__slots__不仅对自己起作用,也会继承父类的__slots__
直接调用/修改类属性和只读使用 @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
student = Student()
student.birth = 2002 # 修改
print(student.birth) # 调用
print(student.age) # 只读
多继承
原理很简单,就是一个类继承一个主类,再添加额外的功能(额外的类),其实一般的设计思路就是如此
class Animal(object):
pass
# 大类:
class Mammal(Animal):
pass
class Bird(Animal):
pass
# 各种动物:
class Dog(Mammal):
pass
class Bat(Mammal):
pass
class Parrot(Bird):
pass
class Ostrich(Bird):
pass
# 额外的功能
class Runnable(object):
def run(self):
print('Running...')
class Flyable(object):
def fly(self):
print('Flying...')
# 多继承
class Dog(Mammal, Runnable):
pass
class Bat(Mammal, Flyable):
pass
定制类
这些功能在廖雪峰这篇文章里面,我基本用不到,了解一下即可
- 调用函数名字(str、repr)
- 类使用 for 循环(iter、next、getitem)
- 调用不存在的属性(getattr)
- 链式调用 API(getattr)
- 调用实例自身(call)
使用枚举类
以星期的枚举类举例:
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
使用案例
>>> 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
使用元类
看不懂,略(●'◡'●)
喜欢划水摸鱼的废人