文章目录
封装
在Python代码中,封装有两层含义:
-
把现实世界中的主体中的属性和方法书写到类的里面的操作即为封装
class Person(): # 封装属性 # 封装方法
-
封装可以为属性和方法添加为私有权限
封装中的私有属性和私有方法
在面向对象代码中,我们可以把属性和方法分为两大类:公有(属性、方法)、私有(属性、方法)
公有属性和公有方法:无论在类的内部还是在类的外部我们都可以对属性和方法进行操作。
但是有些情况下,我们不希望在类的外部对类内部的属性和方法进行操作。我们就可以把这个属性或方法封装成私有形式。
设置私有属性和私有方法的方式非常简单:在属性名和方法名 前面 加上两个下划线 __
即可。
class Person():
def __init__(self, name):
self.name = name
self.__age = 1000
def __show(self):
print(self.__age)
p1 = Person("ii")
p1.__show() # AttributeError: 'Person' object has no attribute '__show'
print(p1.__age) # AttributeError: 'Person' object has no attribute '__age'
那么我们如何在外部调用到私有成员:
class Person():
def __init__(self, name):
self.name = name
self.__age = 1000
def __show(self):
print(self.__age)
def getAge(self):
return self.__age
def getShow(self):
self.__show()
p1 = Person("ii")
print(p1.getAge())
p1.getShow()
继承
class Person():
def __show(self):
print(123)
def run(self):
print("run")
def eat(self):
print("eat")
class Teather(Person):
pass
t1 = Teather()
t1.eat()
t1.run()
# t1.__show() # AttributeError: 'Teather' object has no attribute '__show'
单继承
一个类只能继承自一个其他的类,不能继承多个类。这个类会有具有父类的属性和方法。
单继承特性:传递性
class A():
def show(self):
print("hello")
class B(A):
pass
class C(B):
pass
c = C()
c.show()
注意
-
在Python中,类理论上是不区分大小写的。但是要遵循一定的命名规范:首字母必须是字母或下划线,其中可以包含字母、数字和下划线,而且要求其命名方式采用大驼峰。
-
在Python面向对象代码中,建议在编写父类时,让其自动继承object类。但是其实不写也可以,因为默认情况下,Python中的所有类都继承自object。
多继承
虽然多继承允许我们同时继承自多个类,但是实际开发中,应尽量避免使用多继承,因为如果两个类中出现了相同的属性和方法就会产生命名冲突。
class A():
def show(self):
print("hello")
class B():
pass
class C(A, B):
pass
子类扩展:重写父类属性和方法
重写也叫作覆盖,就是当子类成员与父类成员名字相同的时候,从父类继承下来的成员会重新定义。
思考:
重写父类中的call方法以后,此时父类中的call方法还在不在?
答:还在,只不过是在其子类中找不到了。类方法的调用顺序,当我们在子类中重构父类的方法后,Cat子类的实例先会在自己的类 Cat 中查找该方法,当找不到该方法时才会去父类 Animal 中查找对应的方法。
class A():
def show(self):
print("A")
class B(A):
def show(self):
print("B")
a = A()
a.show()
b = B()
b.show()
super()调用父类属性和方法
super():调用父类属性或方法,完整写法:super(当前类名, self).属性或方法()
,在Python3以后版本中,调用父类的属性和方法我们只需要使用super().属性或super().方法名()
就可以完成调用了。
class A():
def show(self):
print(123);
class B(A):
def showB(self):
super(B, self).show()
a = A()
a.show()
MRO属性或MRO方法:方法解析顺序
MRO(Method Resolution Order):方法解析顺序,我们可以通过类名.__mro__
(返回元组类型)或类名.mro()
(返回元组类型)获得“类的层次结构”,方法解析顺序也是按照这个“类的层次结构”寻找到。
class A():
pass
class B(A):
pass
class C(B):
pass
print(C.__mro__)
print(C.mro())
多态
多态是一种使用对象的方式,子类重写父类方法,调用不同子类对象的相同父类方法,可以产生不同的执行结果。
-
多态依赖继承
-
子类方法必须要重写父类方法
实现
多态:可以基于继承也可以不基于继承,因为即使涉及到的几个类没有继承关系,但是每个类都是继承自object
class A():
def show(self):
print("A")
class B():
def show(self):
print("B")
def run(obj):
obj.show()
run(A())
run(B())
面向对象其他特性
类属性
Python中,属性可以分为实例属性和类属性。
类属性就是类对象中定义的属性,它被该类的所有实例对象所共有。通常用来记录与这类相关的特征,类属性不会用于记录具体对象的特征。
在Python中,一切皆对象。类也是一个特殊的对象,我们可以单独为类定义属性。
定义:
class Person():
age = 0
实例:
class A():
cnt = 0
def __init__(self):
A.cnt += 1
a1 = A()
a2 = A()
a3 = A()
print(f"共存在{A.cnt}个实例")
类方法
为什么需要类方法,在面向对象中,特别强调数据封装性。所以不建议直接在类的外部对属性进行直接设置和获取。所以我们如果想操作类属性,建议使用类方法。
class A():
cnt = 0
def __init__(self):
A.cnt += 1
@classmethod
def getCnt(cls):
return cls.cnt
a1 = A()
a2 = A()
a3 = A()
print(f"共存在{A.getCnt()}个实例")
类方法主要用于操作类属性或类中的其他方法。
静态方法
在开发时,如果需要在类中封装一个方法,这个方法:
-
既不需要访问实例属性或者调用实例方法
-
也不需要访问类属性或者调用类方法
这个时候,可以把这个方法封装成一个静态方法
# 开发一款游戏
class Game(object):
# 开始游戏,打印游戏功能菜单
@staticmethod
def menu():
print('1、开始游戏')
print('2、游戏暂停')
print('3、退出游戏')
# 开始游戏、打印菜单
Game.menu()
单例模式
单例模式属于单例模式。
设计模式就是前人根据实际的问题提出的问题解决方案,我们把这种就称之为设计模式。
在实际的运用中,存在一些类,只需要实例化一个对象,就可以完成其所有的功能操作。所以,如果我们能够通过某些技巧,使得一个类只能开辟一个对象空间的话,这样就可以节省相应的对象资源,这种模式就叫作单例模式。
使用类名()创建对象时,Python的解释器首先会调用__new__方法
为对象分配空间。
__new__方法
__new__是一个由object积累提供的内置的静态方法,主要作用有两个:
-
在内存中为对象分配空间
-
返回对象的引用
Python解析器获得对象的引用后,将引用作为第一个参数,传递给__init__方法
重写__new__方法
的代码非常固定,一定要使用return super().__new__(cls)
,否则Python解释器得不到分配了空间的对象引用,就不会调用对象的初始化方法。
__new__方法
是一个静态方法,在调用时,要求将自身类信息cls作为参数传递到这个方法中,这个方法属于object类中的一个静态方法。
class A():
def __new__(cls, *args, **kwargs):
print("__new__")
return super().__new__(cls)
a = A()
print(a)
注:类属性在内存中是一个特殊的存在,其不用于以前讲过的局部变量(局部变量当函数执行完毕后,其会被内存所销毁)。但是类属性一旦定义,除非对象以及这个类在内存中被销毁了,否则其不会自动销毁。