面向对象编程
面向对象概念
Object Oriented Programming(OOP)
一种程序设计思想,OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。
-
对比
-
面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,会把函数切分为子函数降低系统复杂度。
-
面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。
-
-
三大特点:
-
封装: 使信息隐蔽安全,通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节。
-
继承:子类自动共享父类之间的数据和方法机制
-
多态: 不同对象对同一方法响应的不同行动
-
# eg: 继承,list本身是一个类对象,Mylist继承自list
class Mylist(list):
pass #表示占位符,目前新类不做事情,只继承上面的
list2 = Mylist()
list2.extend('peanutfish')
list2
['p', 'e', 'a', 'n', 'u', 't', 'f', 'i', 's', 'h']
list2.sort()
list2
['a', 'e', 'f', 'h', 'i', 'n', 'p', 's', 't', 'u']
# eg: 多态,调用同一个名字的方法,可以得到不同的结果
# 封装就是通过方法直接可以调用变量但是不需要知道内部实现的细节,更安全
class A:
name = "Mike"
def fun(self):
print('%s is coming' % self.name)
class B:
name = "John"
def fun(self):
print('%s is coming' % self.name)
a = A()
b = B()
a.fun()
Mike is coming
b.fun()
John is coming
面向对象编程
- 比喻:类是图纸,类的实例化是可以住人的房子,通过图纸可以建很多房子,每个房子有自己主人,self就是每个房子的门牌号
- python的self相当于C++的this指针,当对象一个方法被调用时,会将自身作为第一个参数传入self参数,这样就知道是哪个对象在调用方法了
class Ball:
def setName(self, name):
self.name = name
def kick(self):
print('this is %s, who just kicked me...' % self.name)
a.setName('A')
b = Ball()
b.setName('Dog')
a.kick()
this is A, who just kicked me...
b.kick()
this is Dog, who just kicked me...
魔法方法__init__(self)
-
格式
__init__(self, parm1, parm2…)
(注意左右两边双下划线) -
注意到
__init__
方法的第一个参数永远是self
,表示创建的实例本身,因此,在__init__
方法内部,就可以把各种属性绑定到self
,因为self
就指向创建的实例本身。
class Cat:
def __init__(self, name):
self.name = name
def kick(self):
print("this is %s, who kicked me..." % self.name)
c = Cat("BB")*
c.kick()
this is BB, who kicked me...
公有和私有变量
-
name mangling(名字改编,名字重整)
-
在python中定义私有变量只需要在他们前面加上"__"两个下划线(伪私有) ,只有内部可以访问外部不能访问。如果需要外部进行访问或者修改可以添加相应的get xxx 和 set xxx的方法实现。
- 但是python中的属性没有权限控制,都可以被外部调用,通过
_object._Class__attri(前面单下划线,后面双下划线) eg: p._Person__name
- 但是python中的属性没有权限控制,都可以被外部调用,通过
class Dog:
__name = "Cow"
e = Dog()
e.__name
Traceback (most recent call last):
File "<pyshell#47>", line 1, in <module>
e.__name
AttributeError: 'Dog' object has no attribute '__name'
>>> e._Dog__name
'Cow'
继承
-
class DerivedClassname(BaseClassname) – 被继承的类–> 父类、基类(base class),超类(super class)
-
object类时所有类最终都会继承的类
-
如果子类中定义与父类同名的方法或属性,则会自动覆盖父类对应的方法或属性。
-
当子类重写了父类的方法,会覆盖父类的方法,通过下面方法,可以继续继承父类的方法:
- 调用未绑定的父类方法
def __init__(self):
Parent.__init__(self)
…
- 使用super函数
def __init__(self):
super().__init__()
多重继承
class DerivedClassname(Base1, Base2, Base3…)
- 通过多重继承,一个子类就可以同时获得多个父类的所有功能。
- 如果继承两个爹类的类方法相同时,优先继承括号前面的类方法
eg:
class Fish():
def init(self):
self.x = r.randint(0, 10)
self.y = r.randint(0, 10)
def move(self):
self.x -= 1
print("Position is", self.x, self.y)
class Goldfish(Fish):
pass
class Shark(Fish):
def __init__(self):
Fish.init(self) --- 调用未绑定的父类
super().init() -- 使用super函数自动查找父类或者父类的父类定义的同名方法。
self.hungry = True
def eat(self):
if self.hungry:
print("Eating now...")
self.hungry = False
else:
print("Full now...")
组合法
-
把旧类的实例放到一个新类里面,这样就把旧类组合起来了,属于横向的组合,纵向组合就是继承
-
新类使用其他类的实例进行初始化
class Turtle:
def init(self, x):
self.num = x
####
class Fish:
def init(self, x):
self.num = x
####
class Pool:
def init(self, p1, p2):
self.turtle = Turtle(p1) --- 将Turle的实例化直接赋给了Pool的属性
self.fish=Fish(p2)
####
def print(self):
print("We have total %d turtle, %d fish in the pool!" %(self.turtle.num, self.fish.num))
pool = Pool(2, 3)
We have total 2 turtle, 3 fish in the pool!
mix-in
-
方法
https://fishc.com.cn/thread-48888-1-1.html -
在设计类的继承关系时,通常,主线都是单一继承下来的,例如,
Ostrich
继承自Bird
。但是,如果需要“混入”额外的功能,通过多重继承就可以实现,比如,让Ostrich
除了继承自Bird
外,再同时继承Runnable
。这种设计通常称之为MixIn。(Note: 单一继承语言不支持MixIn, 比如Java) -
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn): pass
MixIn的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系。
Python自带的很多库也使用了MixIn。举个例子,Python自带了
TCPServer
和UDPServer
这两类网络服务,而要同时服务多个用户就必须使用多进程或多线程模型,这两种模型由ForkingMixIn
和ThreadingMixIn
提供。通过组合,我们就可以创造出合适的服务来。比如,编写一个多进程模式的TCP服务,定义如下:
class MyTCPServer(TCPServer, ForkingMixIn): pass
限制实例的属性
使用__slots__
但是,如果我们想要限制实例的属性怎么办?比如,只允许对Student实例添加name
和age
属性。
为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__
变量,来限制该class实例能添加的属性:
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__
。