面向对象的三大特征:
继承:大致理解就是拓展原来的类,在已有类的基础上新添加一些独特的新方法,新属性。和利用动态特性给类绑定新方法,新属性一个效果。
封装:隐藏细节,人为叫法,保护类里面的数据不容易被类外部的方法或者函数随意修改。
多态:指同一个方法,由不同的对象调用会产生不同的效果。同样是叫声,狗是汪汪叫,猫是喵喵叫。猫狗都是动物类,都会叫,猫和狗又是动物类的较具体的对象,但是他们叫的方法产生的声音不同。
目录
一,继承
1,单继承和多继承
继承是指新建一个类,这个类的一部分内容和另外一个类差不多,那么就可以照抄另一个类的内容,然后再添加自己的内容。被抄袭的那个类我们称为基类、父类,新的类我们称为派生类或者子类。
继承实现了代码的复用,没有必要重新发明轮子。
继承的语法格式如下:
class 子类类名(父类1[,父类2,...]):
子类类体
class A(): # 定义时括号里虽然啥也没写,但是系统默认object存在,即class A()的完整形式是class A(object)。object是根类。
pass
class Age():
num = 100
def __init__(self):
self.age = 18
def show(self):
print(self.num, self.age)
# Student类继承Age类,然后Student类就不需要写关于年龄age部分的代码了
class Student(Age): # 实际上是class Student(Age,object):,因为默认继承object类,这个类提供了很多内置函数
def __init__(self):
self.name = 'xiaoming'
super().__init__() # 调用父类方法,把父类的__init__方法定义的实例属性age拿来
def show(self): # 父类有个一模一样的方法,但是父类的方法不是我们需要的功能,重新写一个,使变量show与新写的方法绑定
print(self.name, self.age)
s1 = Student()
s1.show()
# xiaoming 18
多继承,就是一个新类抄了多个已有类。
2,如何精准地调用父类的__init__方法
我们知道如果一个类里边不写__init__方法的话,系统会自动帮我们创建一个__init__方法来执行,其实执行的是object类的__init__方法。
除了默认继承object之外,假如还继承了其他类,而我们又不写__init__方法,系统会怎么做呢?答案是:调用第一个继承的类的__init__方法。也就是调用类名后面小括号里的第一个类的__init__方法。
class A():
def __init__(self):
print('A')
class B():
def __init__(self):
print('B')
class C(A, B):
pass
c = C()
# A
class C(B, A):
pass
c1 = C()
# B
假如我们在新类里面写了__init__方法了呢?那么就会调用我们写的__init__方法,而不会去调用父类的__init__方法。但是,如果父类的__init__方法没有调用,父类__init__方法里定义的实例属性在新类里又得不到,这个时候需要手动调用父类的__init__方法。使用super()函数,他会自动去寻找新类抄袭的的所有父类中的__init__方法去执行,到底去执行哪一个父类的的__init__的方法呢?可以由super()函数的参数决定。
class A():
def __init__(self):
print('A')
class B():
def __init__(self):
print('B')
class C(A, B): # 记住C->A->B这个顺序
def __init__(self):
super(C, self).__init__() # 这里的参数是C,则去找A类的__init__方法
super(A, self).__init__() # 这里的参数是A,则去找B类的__init__方法
super(B, self).__init__() # 这里的参数是B,则去找顺序中B类的下一个类,很明显B类之后没有类了,其实不然,B类之后还存在object类
c = C()
# 为什么会按照所给参数的下一个类来执行__init__呢?看一下下面这个类的方法
print(C.mro())
# [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
# 因为我们在新类里面已经写了__init__方法,手动调用父类的__init__方法时会去调用继承的第一个父类,也就是A类。
# 然而这个super()有点反常,他不按套路出牌,他回去调用所给参数的下一个类的__init__方法。
# 假如我们把参数写成C,按照平常思维会让他去找C类中的__init__方法,那他为啥要去找自己的__init__方法呢?也许这就是调用下一个的原因
另外也可以用“父类.__init__()”的形式来指定调用哪一个父类的__init__方法。
class A():
def __init__(self):
print('A')
class B():
def __init__(self):
print('B')
class C(A, B): # 记住C->A->B这个顺序
def __init__(self):
A.__init__(self)
B.__init__(self)
c = C()
# A
# B
3,子类能从父类那里继承什么?
一个新类想要抄袭已经存在的类的内容,但是并不是全部都可以抄过来,有些东西是无法抄来的,也就是有些数据和代码是无法继承的。
python中,子类能够继承父类所有东西,唯一不能继承的就是父类的__init__构造方法。可以这么理解,父类中除了__init__方法包含的所有代码之外,都能被继承。比如类方法,类变量,定义的实例方法,实例属性,实例变量里的局部变量,不管私不私有都能被继承。
1,当我们没有定义__init__方法时,解释器会默认去调用第一个继承的父类的__init__函数
2,当我们定义了新的__init__函数,由于自己已经有了__init__方法,就不需要父类的了。由于没有继承父类的__init__方法,需要手动调用。
class A():
num = 100
__num = 100
@classmethod
def f1(cls):
print(666)
@staticmethod
def f2(cls):
print(666)
def __init__(self):
self.name = 'lihua'
self.__age = 18
def show(self):
print(666)
class B(A):
def __init__(self):
print('我继承了哪些内容呢?打印一下')
print(dir(B))
"""
我继承了哪些内容呢?打印一下
[@'_A__num'@, '__class__', '__delattr__', '__dict__', '__dir__',
'__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
'__gt__', '__hash__', '__init__', '__init_subclass__', '__le__',
'__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
'__weakref__', @'f1', 'f2', 'num', 'show'@]
"""
B()
4,方法重写
当子类继承父类的方法之后,很多父类的方法并不是子类所想要的,子类可以对这些继承的到的方法进行修改,添加或者删除一些代码,这就是子类重写父类的方法,又叫方法重写。
class A():
def __init__(self):
self.name = 'lihua'
self.__age = 18
def show(self):
print(666)
class B(A):
def __init__(self):
pass
def show(self): # 重写父类的show方法,打印888而不是666
print(888)
B.show(B())
# 888
使用类对象的mro()方法,获取这个类继承了哪些父类和继承顺序。
class A: pass
class B(A): pass
class C(B): pass
print(C.mro())
# [<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
二,object根类
object 类
它是所有类的父类,因此所有的类都有 object 类的属性和方法。在object类的方法中,有许多我们常用却不知道来路的方法。
print(dir(object))
"""
['__class__', '__delattr__', '__dir__', '__doc__',
'__eq__', '__format__', '__ge__', '__getattribute__',
'__gt__', '__hash__', '__init__', '__init_subclass__',
'__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__']
"""
重写 __str__() 方法
object 有一个 __str__() 方法,用于返回一个对于“对象的描述”。内置函数 str(对象),调用的就是__str__() 。__str__() 经常用于 print() 方法,帮助我们查看对象的信息。 __str__() 可以重写。
class A():
def __str__(self):
return '666'
print(A()) # 打印A()这个对象的信息
# 666
MRO方法解析顺序
Python支持多继承,如果父类中有相同名字的方法,在子类没有指定父类名时,解释器将“从左向右”按顺序搜索。MRO(Method Resolution Order):方法解析顺序。 我们可以通过 mro() 方法获得“类的层次结构”,方法解析顺序也是按照这个“类的层次结构”寻找的。
也就是说,一个类继承了多个父类,这些父类难免会出现相同的方法,比如每个父类都有__init__方法,那么解释器会去找哪一个父类的__init__方法执行呢?按照的就是MRO的顺序。当然,我们可人为手动选择去调用父类的方法,或者交给解释器按照MRO的顺序自动帮我们找。
class A:
def show(self):
print('A')
class B:
def show(self):
print('B')
class C:
def show(self):
print('C')
class D(A, B, C):
def __init__(self):
super(D, self).show() # 这里不写self的原因是super()函数里面已经补充了self,再写一次会报错
super(A, self).show()
super(B, self).show()
A.show(self)
B.show(self)
C.show(self)
# 使用super()函数和类名.方法名都可以选择性的调用父类同名函数
d = D()
# A
# B
# C
# A
# B
# C
特殊方法和运算符重载
Python的运算符实际上是通过调用对象的特殊方法实现的。
a = 1
b = 1
sum1 = a.__add__(b) # a+b实际上是解释器调用了a.__add__(b)
sum2 = a.__add__(1)
print(sum1, sum2)
# 2 2
一些常见操作的本质如下:
__init__ :构造方法,用于对象初始化: p = Person()
__del__ :析构方法,用于对象回收,del obj
__repr__ , __str__ :永固打印,转换 ,print()
__call__ :函数调用 a()
__getattr__ :.点号运算 a.xxx
__setattr__ :属性赋值 a.xxx = value
__getitem__ :索引运算 a[key]
__setitem__ :索引赋值 a[key]=value
__len__ :长度 len(a)
li = [1, 2, 3]
print(li.__getitem__(0))
# 1
def test():
print(666)
test.__call__()
# 666
三,组合
组合也能实现代码地复用,与继承的效果一样,只是语法实现方式和实现逻辑不一样。
“组合”核心是“将父类对象作为子类的属性”。
继承是is-a 关系从而实现子类拥有的父类的方法和属性。 is-a 关系指的是类似这样的关系:狗是动物。狗类就应该继承动物类。
组合是 has-a 关系,使用“组合”,也能实现一个类拥有另一个类的方法和属性。 has-a 关系指的是这样的关系:手机拥有CPU。
组合的实现方式是把其他类的对象作为参数传入一个新的类进行初始化。类似于组装,拿给你车轮,车架,发动机让你组装一辆车。
class Chelun():
def __init__(self):
self.brand = '三星米其林'
def how_to_use(self):
print('一圈一圈的转')
class Motor():
def __init__(self):
self.type = 'V12'
def how_to_work(self):
print('喝油')
class Chejia():
def __init__(self):
pass
def what_make(self):
print('炭纤维做的')
class Car():
def __init__(self, chelun_obj, motor_obj, chejia_obj):
self.chelun = chelun_obj
self.motor = motor_obj
self.chejia = chejia_obj
car = Car(Chelun(), Motor(), Chejia()) # 把三个实例对象传给Car类进行绑定
car.chelun.how_to_use()
car.motor.how_to_work()
car.chejia.what_make()
# 一圈一圈的转
# 喝油
# 炭纤维做的
组合与我们的思维更为相似,即分开之后再组装。
四,设计模式之工厂模式
工厂模式实现了创建者和调用者的分离,使用专门的工厂类将选择实现类、创建对象进行统一的管理和控制。
# 工厂模式
class Factory():
def __init__(self, ):
pass
def create_ins(self, lei_type):
if lei_type == type:
if lei_type == A:
return A()
elif lei_type == B:
return B()
return None
class A():
def __init__(self):
pass
def show(self):
print('a')
class B():
def __init__(self):
pass
def show(self):
print('b')
factory = Factory()
obj_a = factory.create_ins(A)
obj_b = factory.create_ins(B)
obj_a.show()
obj_b.show()
即把创建对象的工作交给一个统一的类去处理。
五,设计模式之单例模式
单例模式,是指一个类只创建一个对象。如果重复创建这个类的对象,则会返回第一个创建的对象。
import threading
import time
class Singlemode():
instance = None
lock = threading.RLock()
def __init__(self):
pass
def __new__(cls, *args, **kwargs):
if cls.instance:
return cls.instance
with cls.lock:
if cls.instance:
return cls.instance
cls.instance = object.__new__(cls)
time.sleep(0.1)
return cls.instance
obj = []
def creatobj():
global obj
for i in range(10): # 创建10个对象,然后存在列表obj里面。
obj.append(Singlemode())
t1 = threading.Thread(target=creatobj)
t1.start()
t1.join()
for i in obj:
print(i)
# <__main__.Singlemode object at 0x00000256F7F1A808>
# <__main__.Singlemode object at 0x00000256F7F1A808>
# <__main__.Singlemode object at 0x00000256F7F1A808>
# <__main__.Singlemode object at 0x00000256F7F1A808>
# <__main__.Singlemode object at 0x00000256F7F1A808>
# <__main__.Singlemode object at 0x00000256F7F1A808>
# <__main__.Singlemode object at 0x00000256F7F1A808>
# <__main__.Singlemode object at 0x00000256F7F1A808>
# <__main__.Singlemode object at 0x00000256F7F1A808>
# <__main__.Singlemode object at 0x00000256F7F1A808>