__str__
用print打印对象时,会自动调用
class Test:
def __init__(self,name):
self.name = name
# 用print打印对象时,会自动调用
def __str__(self):
return f"姓名name的值是{self.name}"
a = Test("zhangsan")
print(a)
结果:
姓名name的值是zhangsan
#不用__str__时:
#打印<__main__.Test object at 0x000001E3058A5050>
封装
面向对象的三大特征:封装、继承、多态(有的人认为抽象也是)
在面向对象中,认为定义在类中的属性外界可以更改是不安全的,封装指一种安全机制,不让外界直接修改或操作属性,因此,将属性私有化(封装),不让外界访问,如果要访问提供公开的方法:(getter、setter)
思想:如何将现实世界的事物描述成程序中的类私有化成员(属性、方法) sys模块查看
在成员变量或者成员方法前面加 _ _ 即可,私有成员类对象无法访问,而类中的其他成员可以使用
封装的目的:
- 封装数据:保护隐私
- 封装方法:隔离复杂度(只保留部分接口对外使用)
封装的方式
私有属性和方法:
-
私有成员变量:变量名以__开头(2个下划线)
-
私有成员方法:方法名以__开头(2个下划线)
class Phone:
__current_voltage = None # 当前电压 私有成员变量
def call_by_5g(self):
print("5g通话已开启")
def __keep_single_core(self): # 私有成员方法
print("让CPU以单核模式运行以节省电量")
在Python中,可以使用_和__来定义属性的访问级别:
- 以_开头的属性被视为受保护的属性,即它们不应该在类外部被直接访问。但是,它们可以在类的子类和内部方法中被访问。
- 以__开头的属性被视为私有属性,即它们不应该在类的外部被访问。但是,它们也可以在类的内部方法中被访问。
在类外部不能访问类内受保护的属性或方法(python规则)
例1:
class Phone:
__current_voltage = None #当前电压
def call_by_5g(self):
print("5g通话已开启")
def __keep_single_core(self):
print("让cPU以单核模式运行以节省电量")
phone = Phone() # 创建对象
phone.__current_voltage=33 # 私有变量赋值 不报错,但无效
print(phone.__current_voltage)#获取私有变量值 报错,无法使用
对于私有属性,如果我们要在类外进行访问的话,会抛出 AttributeError 的错误
例2:
class Student:
name=None
age=None
__userroot="123456"
def __init__(self,name,age):
self.name=name
self.age=age
# print(f"{self.name}{self.age}")
def start(self):
if self.__userroot=="123456":
print("开始运行了")
def get_password(self): #方法:获取self.__userroot
return self.__userroot
def set_password(self,password): ##方法:修改self.__userroot
self.__userroot= password
name=Student("12",12)
name.start()
#print(name.__userroot) #私有成员外界访问不到
# python时动态语言,此时userroot私有相当于没有,会动态创建
name.userroot="123"
# 外界要访问私有化成员 get,set方法,在类中定义
name.set_password("222222")
print(name.get_password())
结果:
开始运行了
222222
python时动态语言,类中没有的属性,外边会动态创建,不会报错。
私有成员无法被类对象使用,但是可以被其它的成员使用
class Phone:
__current_voltage = 0.5
def __keep_single_core(self):
print("让CPU以单核模式运行以节省电量")
def call_by_5g(self):
if self.__current_voltage >= 1: #可以使用
print("5g通话已开启")
else:
self.__keep_single_core() #可以使用
print("让CPU以单核模式运行以节省电量")
获取方法:
- 设置公开的方法:(getter、setter)方法
- 全局方法(property)
class Person:
def __init__(self,name,age):
self.__name = name
self.__age = age
def getter_name(self):
return self.__name
def setter_name(self,name):
self.__name = name
def getter_age(self):
return self.__age
def setter_age(self,age):
self.__age = age
def print_01(self):
print(f"姓名是{self.__name},年龄是{self.__age}")
age= property(getter_age,setter_age) #全局方法,与def并列
p1 = Person("张三",21)
p1.age = 1000
print(p1.age)
p1.print_01()
结果:
1000
姓名是张三,年龄是1000
案例:
class Phone:
# 设计私有成员变量
__is_5g_enable = True # 5G状态
# 私有成员方法
def __check_5G(self):
if self.__is_5g_enable == True:
print("5G开启")
else:
print("5G关闭,使用4G网络")
# 提供公开成员方法
def call_by_5G(self):
self.__check_5G()
print("正在通话中")
phone = Phone()
phone.call_by_5G()
结果:
5G开启
正在通话中
属性装饰器
属性装饰器是一种特殊的函数,它可以被用来修改属性的访问方式。在Python中,我们可以使用@property 和 @属性名.setter装饰器来定义属性的访问器和修改器
- @property装饰器:用于定义属性的访问器,它会将属性定义为一个只读属性。当外部代码试图修改这个属性时,会触发一个AttributeError异常。
- @属性名.setter装饰器:用于定义属性的修改器,它可以让外部代码修改这个属性的值。当外部代码试图读取这个属性时,会触发一个AttributeError异常。
案例
class MyClass:
def __init__(self,value):
self. __value = value
@property #读取
def value(self):
return self. __value
@value.setter #修改
def value(self,new_value):
self. __value = new_value
a = MyClass("哈哈哈哈")
print(a.value)
a.value = "啦啦啦啦"
print(a.value)
结果:
哈哈哈哈
啦啦啦啦
继承
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。
通过继承创建的新类称为子类或派生类,被继承的类称为基类、父类或超类
目的:大量减少代码冗余
__class__
实例出来的对象中存在一个属性__class__指向类
class Test:
def print_01(self):
pass
a = Test()
print(a.__class__)
结果:
<class '__main__.Test'>
继承的基本语法
class 派生类名(基类名)
...
例:
Object:是python中最顶层的类
class A(object):
xxxx
class B(A): #A为父类:基类 B为子类:派生类
pass
单继承案例
class Parent: # 定义父类
parentAttr = 100
def __init__(self):
print("调用父类构造函数")
def parentMethod(self):
print('调用父类方法')
def setAttr(self, attr):
Parent.parentAttr = attr
def getAttr(self):
print("父类属性 :", Parent.parentAttr)
class Child(Parent): # 定义子类
def __init__(self):
print("调用子类构造方法")
def childMethod(self):
print('调用子类方法')
c = Child() # 实例化子类
c.childMethod() # 调用子类的方法
c.parentMethod() # 调用父类方法
c.setAttr(200) # 再次调用父类的方法 - 设置属性值
c.getAttr() # 再次调用父类的方法 - 获取属性值
运行结果:
调用子类构造方法
调用子类方法
调用父类方法
父类属性 : 200
多继承
基本语法:
class 类名(父类1,父类2,……,父类N):
案例:
class Camera:
def take_photo(self):
print("我现在可以照相了")
class Mp4:
def play_music(self):
print("我现在能听歌了")
class Telephone(Camera,Mp4):
def call(self):
print("我能打电话")
def answer(self):
print("我能接电话")
tel = Telephone()
tel.play_music() #调用父类的方法
tel.take_photo()
结果:
我现在能听歌了
我现在可以照相了
多继承注意事项
- 多个父类中,如果有同名的成员,那么默认以继承顺序(从左到右)为优先级。
- 即:先继承的保留,后继承的被覆盖
pass
pass是占位语句,用来保证函数(方法)或类定义的完整性,表示无内容,空的意思
复写:
觉得父类的方法不合适,在子类中定义一个与父类方法名相同的方法
复写案例
class Father:
def play_game(self):
print("超级玛丽")
print("小霸王")
def drink(self):
print("喝水")
class Son(Father):
def play_game(self):
print("王者荣耀")
print("吃鸡")
son = Son()
son.play_game() #子类有则不去父类寻找
son.drink()
结果:
王者荣耀
吃鸡
喝水
调用父类同名成员
一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员
如果需要使用被复写的父类的成员,需要特殊的调用方式:
方式1:
调用父类成员
使用成员变量:父类名.成员变量
使用成员方法:父类名.成员方法(self)
方式2:
使用super()调用父类成员
使用成员变量:super().成员变量
使用成员方法:super().成员方法()
实例1:
class Phone:
IMEI = None # 序列号
producer="ITCAST" # 厂商
def call_by_5g(self):
print("父类的5g通话")
class MyPhone(Phone):
proucer = "ITHEIMA"
def call_by_5g(self):
# 方式1调用父类成员
print(f"父类的品牌是:{Phone.producer}")
Phone.call_by_5g(self)
#方式2调用父类成员
print(f"父类的品牌是:{super().producer}")
super().call_by_5g()
print("子类的5g通话")
c = MyPhone()
print(c.call_by_5g())
结果:
父类的品牌是:ITCAST #方法1
父类的5g通话
父类的品牌是:ITCAST #方法2
父类的5g通话
子类的5g通话
None
实例2:
class Father(object):
def __init__(self,uname,age):
self.uname = uname
self.age = age
class Son(Father):
def __init__(self,uname,age,collage):
# self.uname = uname
# self.age = age
super().__init__(uname,age)
self.collage = collage
def __str__(self):
return f"姓名是{self.uname},年龄是{self.age},大学是{self.collage}"
son = Son("王麻子",91,"家里蹲")
print(son)
结果:
姓名是王麻子,年龄是91,大学是家里蹲
多态
什么是多态
举一个现实中的例子,同样的一件事情,不同的人处理起来,他们的实现过程是完全不同的,会表现出不同的形态。比如:都是吃饭这个事情,中国人表现的是用筷子吃饭,而美国人表现的是用叉刀吃饭。这个就是相同的事情,表现出了不同的“形态”。
实例
定义了一个 animal 类,其中有一个方法 speak 表示动物发出声音
定义了两个子类——狗和猫,它们都继承了 animal 类并且复写了 speak 方法
- 定义了一个函数,该函数需要接收一个类型为 animal 的对象
- 调用 speak 方法,动物叫
- 创建狗和猫这两个子类的对象
- 调用函数时,传入狗和猫,然后输出“汪汪汪”和“喵喵喵”
这就是多态的体现,我们完成了某个具体的行为——即调用函数,但是我们传入了不同的对象,从而得到了不同的状态。比如,我们传入狗就会输出“汪汪汪”,传入猫就会输出“喵喵喵”
抽象类(接口)
- 抽象类:含有抽象方法的类称之为抽象类
- 抽象方法:方法体是空实现的(pass)称之为抽象方法
父类Animal的speak方法,是空实现
这种设计的含义是:
- 父类用来确定有哪些方法
- 具体的方法实现,由子类自行决定
抽象类就好比定义一个标准,包含了一些抽象的方法,要求子类必须实现
isinstance( )方法
描述
sinstance() 函数来判断一个对象是否是一个已知的类型,类似 type()。
isinstance() 与 type() 区别:
- type() 不会认为子类是一种父类类型,不考虑继承关系。
- isinstance() 会认为子类是一种父类类型,考虑继承关系。
4如果要判断两个类型是否相同推荐使用 isinstance()
语法
isinstance(object, classinfo)
- object -- 实例对象。
- classinfo -- 可以是直接或间接类名、基本类型或者由它们组成的元组。
如果对象的类型与参数二的类型(classinfo)相同则返回 True,否则返回 False
案例
a = 10086
isinstance (a,int) # true
isinstance (a,str) # false
isinstance (a,(str,int,list))# 只要满足元组类型中的其中一个即可,答案是满足,所以为true
class A:
pass
class B(A):
pass
print(isinstance(A(), A)) # True
print(type(A()) == A) # True
print(isinstance(B(), A)) # True
print(type(B()) == A) # False