OOP
面向对象程序设计(Object Oriented Programming)作为一种新方法,其本质是以建立模型体现出来的抽象思维过程和面向对象的方法。
定义类
类把数据与功能绑定在一起。创建新类就是创建新的对象 类型,从而创建该类型的新 实例 。类实例支持维持自身状态的属性,还支持(由类定义的)修改自身状态的方法。
最简单的类定义形式如下:
class ClassName:
<statement-1>
.
.
.
<statement-N>
抽象基类的定义
class ClassName:
@abstractmethod # 装饰器
def function(self):
pass
#要点是你使用@abstractmethod来将方法标记为抽象的——在子类中必须实现的方法。
该模块提供了在 Python 中定义 抽象基类 (ABC) 的组件。
from abc import ABC
class MyABC(ABC):
pass
类的范例
class Cat:
def __init__(self, name, age, color, owner):
self.name = name
self.age = age
self.color = color
self.__owner = owner
def feedTheCat(self):
print(self.name, 'has been fed')
def callOwner(self):
print(r"its owner is ", self.__owner)
myCat = Cat('xuhu', '4', 'yellow', 'pin') # 实例化
myCat.feedTheCat() # 调用对象的方法
# output: xuhu has been fed
print('age of {0} is {1}'.format(myCat.name, myCat.age)) # 属性引用
# output: age of xuhu is 4
myCat.callOwner() # 调用对象的方法仍然可以访问私有变量
# output: its owner is pin
print(r"its owner is ", myCat._Cat__owner) # 非正规的方法访问私有变量
# output: its owner is pin
print(r"its owner is ", myCat.owner) # 私有变量不能调用
# output:
# Traceback (most recent call last):
# File "E:\PyCode\Python_Study\LsitDemo.py", line 22, in <module>
# print(r"it's is ", myCat.__owner) # 私有变量不能调用
# AttributeError: 'Cat' object has no attribute '__owner'
类对象支持两种操作:属性引用和实例化。
- 要让方法或属性成为私有的(不能从外部访问),只需让其名称以两个下划线打头即可。
- from module import *不会导入以一个下划线打头的名称。
- 在类定义中,对所有以两个下划线打头的名称都进行转换,即在开头加上一个下划线和类名。(通过 实例._类名__私有变量(方法) 可以非正常访问)
属性:
- 可将属性定义为私有。私有属性不能从对象外部访问,而只能通过存取器方法来访问。
- 让方法或属性成为私有的(不能从外部访问),只需让其名称以两个下划线打头即可。
- 函数property。实际上,调用函数property时,还可不指定参数、指定一个参数、指定三个参数或指定四个参数。
- __getattribute__(self, name):在属性被访问时自动调用(只适用于新式类)。在__getattribute__中访问当前实例的属性时,唯一安全的方式是使用超类的方法__getattribute__(使用super)。
__getattr__(self, name):在属性被访问而对象没有这样的属性时自动调用。仅当没有找到指定的属性时,才会调用方法__getattr__。
__setattr__(self, name, value):试图给属性赋值时自动调用。即便涉及的属性不是你需要的,也将调用方法__setattr__。
__delattr__(self, name):试图删除属性时自动调用。
class Rectangle:
def __init__(self):
self.width = 0
self.height = 0
def set_size(self, size):
self.width, self.height = size
def get_size(self):
return self.width, self.height
size = property(get_size, set_size)
myRectangle = Rectangle()
# 普通访问
myRectangle.width = 10
myRectangle.height = 5
print(myRectangle.width, myRectangle.height)
# output: 10 5
# 存取方法访问
myRectangle.set_size((100, 50))
print(myRectangle.get_size())
# output: (100, 50)
# 函数property访问
myRectangle.size = (1000, 500)
print(myRectangle.size)
# output: (1000, 500)
class Rectangle:
def __init__(self):
self.width = 0
self.height = 0
def set_size(self, size):
self.width, self.height = size
def get_size(self):
return self.width, self.height
size = property(get_size, set_size)
myRectangle = Rectangle()
# 普通访问
myRectangle.width = 10
myRectangle.height = 5
print(myRectangle.width, myRectangle.height)
# output: 10 5
# 存取方法访问
myRectangle.set_size((100, 50))
print(myRectangle.get_size())
# output: (100, 50)
# 函数property访问
myRectangle.size = (1000, 500)
print(myRectangle.size)
# output: (1000, 500)
方法:
- 实例对象的有效方法名称依赖于其所属的类。方法和函数的区别表现在前一节提到的参数self上。
- 静态方法和类方法是这样创建的:将它们分别包装在staticmethod和classmethod类的对象中。静态方法的定义中没有参数self,可直接通过类来调用。
实例化及属性、方法的覅用
classInstance=ClassName()
classInstance.propertyName
classInstance.function()
迭代器
- 通过对可迭代对象调用内置函数iter,可获得一个迭代器。
it = iter([1, 2, 3])
更正规的定义是,实现了方法__iter__的对象是可迭代的,而实现了方法__next__的对象是迭代器。
该函数返回一个定义了 __next__() 方法的迭代器对象,此方法将逐一访问容器中的元素。 当元素用尽时,__next__() 将引发 StopIteration 异常来通知终止 for
循环。
- 如果要使用类的迭代器,必须在类中实现 iter() 和 next() 方法
class Cat:
def __init__(self, name, age, color, owner):
self.name = name
self.age = age
self.color = color
self.__owner = owner
def feedTheCat(self):
print(self.name, 'has been fed')
def callOwner(self):
print(r"its owner is ", self.__owner)
myCat = Cat('xuhu', '4', 'yellow', 'pin') # 实例化
myCat.feedTheCat() # 调用对象的方法
# output: xuhu has been fed
print('age of {0} is {1}'.format(myCat.name, myCat.age)) # 属性引用
# output: age of xuhu is 4
myCat.callOwner() # 调用对象的方法仍然可以访问私有变量
# output: its owner is pin
print(r"its owner is ", myCat._Cat__owner) # 非正规的方法访问私有变量
# output: its owner is pin
print(r"its owner is ", myCat.owner) # 私有变量不能调用
# output:
# Traceback (most recent call last):
# File "E:\PyCode\Python_Study\LsitDemo.py", line 22, in <module>
# print(r"it's is ", myCat.__owner) # 私有变量不能调用
# AttributeError: 'Cat' object has no attribute '__owner'
生成器
生成器 是一个用于创建迭代器的简单而强大的工具。 它们的写法类似于标准的函数,但当它们要返回数据时会使用 yield 语句。
每次在生成器上调用 next() 时,它会从上次离开的位置恢复执行(它会记住上次执行语句时的所有数据值)。
一个显示如何非常容易地创建生成器的示例如下:
def reverse(data):
for index in range(len(data)-1, -1, -1):
yield data[index]
for char in reverse('golf'):
print(char)
# output: f
# output: g
# output: o
# output: g
- 生成器是一种使用普通函数语法定义的迭代器。
每次使用yield生成一个值后,函数都将冻结,即在此停止执行,等待被重新唤醒。被重新唤醒后,函数将从停止的地方开始继续执行。
生成器由两个单独的部分组成:生成器的函数和生成器的迭代器。生成器的函数是由def语句定义的,其中包含yield。生成器的迭代器是这个函数返回的结果。
- 用生成器和外部之间的通信渠道:
外部世界:外部世界可访问生成器的方法send,这个方法类似于next,但接受一个参数(要发送的“消息”,可以是任何对象)。
生成器:在挂起的生成器内部,yield可能用作表达式而不是语句。换而言之,当生成器重新运行时,yield返回一个值——通过send从外部世界发送的值。如果使用的是next,yield将返回None。
请注意,仅当生成器被挂起(即遇到第一个yield)后,使用send(而不是next)才有意义。
def repeater(value):
while True:
new = (yield value)
if new is not None: value = new
r = repeater(42)
print(next(r))
# output: 42
r.send("Hello, world!")
print(next(r))
# output: Hello, world!
- 生成器还包含另外两个方法。
方法throw:用于在生成器中(yield表达式处)引发异常,调用时可提供一个异常类型、一个可选值和一个traceback对象。
方法close:用于停止生成器,调用时无需提供任何参数。
OOP的特征
多态、封装、继承
多态:可对不同类型的对象执行相同的操作。大致意味着即便你不知道变量指向的是哪种对象,也能够对其执行操作,且操作的行为将随对象所属的类型(类)而异。
class MyCalc:
@staticmethod # 静态方法
def clacMul( x, y):
return x * y
print(MyCalc.clacMul(2, 2))
# output: 4
print(MyCalc.clacMul(2, 2.0))
# output: 4.0
print(MyCalc.clacMul('Abc', 2))
# output: AbcAbc
封装:封装(encapsulation)指的是向外部隐藏不必要的细节。
class Cat:
def __init__(self, name):
self.__name = name
def setName(self, name):
self.__name = name
def getName(self):
return self.__name
def show(self):
print(self.__name)
myCat = Cat('xuHu') # 实例化,不能直接访问__name,被封装起来了
myCat.show()
# output: xuHu
myCat.setName('xuDuoMiao')
print(myCat.getName())
# output: xuDuoMiao
继承:可基于通用类创建出专用类。
- 派生类定义的语法如下所示:
class DerivedClassName(BaseClassName): # 名称 BaseClassName 必须定义于包含派生类定义的作用域中。
<statement-1>
.
.
.
<statement-N>
class DerivedClassName(modname.BaseClassName): # 基类在其他模块中
<statement-1>
.
.
.
<statement-N>
# BaseClassName :被称为超类或基类。
- 带有多个基类的类定义语句如下所示:
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
.
.
.
<statement-N>
方法解析顺序会动态改变以支持对 super() 的协同调用。 这种方式在某些其他多重继承型语言中被称为后续方法调用,它比单继承型语言中的 super 调用更强大。
如果多个超类以不同的方式实现了同一个方法(即有多个同名方法),必须在class语句中小心排列这些超类,因为位于前面的类的方法将覆盖位于后面的类的方法
- 派生类可能会重写其基类的方法。
在派生类中的重载方法实际上可能想要扩展而非简单地替换同名的基类方法。有一种方式可以简单地直接调用基类方法:即调用 BaseClassName.methodname(self, arguments)
class Animal:
def __init__(self, name):
self.name = name
def show(self):
print(self.name)
def showSelf(self):
print(self.name)
class Cat(Animal):
def __init__(self, name, kind): # 重写init方法
super().__init__(name) # 调用基类的方法,这里super().__init__(name)可改成Animal.__init__(self,name)
self.kind = kind
def showSelf(self): # 派生类中重写方法
print('The king of {0} is {1}'.format(self.name, self.kind))
myCat = Cat('xuHu', 'British shorthair cat')
myCat.show() # 调用继承自基类的方法.
# output: xuHu
myCat.showSelf() # 调用派生类重写的方法
# output: The king of xuHu is British shorthair cat
- Python可被用于继承机制的内置工具:
-
使用 isinstance() 来检查一个实例的类型:
isinstance(obj, int)
仅会在obj.__class__
为 int 或某个派生自 int 的类时为True
。 -
使用 issubclass() 来检查类的继承关系:
issubclass(bool, int)
为True
,因为 bool 是 int 的子类。 但是,issubclass(float, int)
为False
,因为 float 不是 int 的子类。 - 如果你有一个类,并想知道它的基类,可访问其特殊属性__bases__。
- 如果你要获悉对象属于哪个类,可使用属性__class__。
class Animal:
def __init__(self, name):
self.name = name
def show(self):
print(self.name)
def showSelf(self):
print(self.name)
class Cat(Animal):
def __init__(self, name, kind): # 重写init方法
super().__init__(name) # 调用基类的方法,这里super().__init__(name)可改成Animal.__init__(self,name)
self.kind = kind
def showSelf(self): # 派生类中重写方法
print('The king of {0} is {1}'.format(self.name, self.kind))
print(issubclass(Cat, Animal)) # 参数1是参数2的子类?
# output: True
print(issubclass(Animal, Cat)) # 参数1是参数2的子类?
# output: False
print(Cat.__bases__) # 获取Cat的基类
# output: (<class '__main__.Animal'>,)
myCat = Cat('xuHu', 'British shorthair cat')
print(isinstance(myCat, Cat)) # 参数1是参数2的实例?
# output: True
print(isinstance(myCat, Animal)) # 参数1是参数2的基类的实例?
# output: True
print(myCat.__class__) # 获取myCat的类所属
# output: <class '__main__.Cat'>