面向对象的编程(2)-Day09
方法的动态性
-python是动态语言,可以动态的为类添加新的方法
-动态的修改类的已有的方法
#测试方法的动态性
class Person:
def work(self):
print("努力上班!")
def play_game(self):
print("{0}玩游戏".format(self))
def work2(s):
print("好好工作,努力上班!")
Person.play = play_game
Person.work = work2
p = Person()
p.play()
p.work()
上述代码显示,Person新增了play_game的方法,并且新增的方法work2替换掉了原有的work方法。
私有属性和私有方法–实现封装
- 约定:两个下划线__开头的属性是私有的private,其余的是公共public
- 类的内部可以访问私有属性和方法
- 类的外部不能直接访问私有属性和方法
- 类的外部需要通过"_类名__私有属性(方法)名称" 来访问私有属性
class Employee:
__company = "百战程序员" #私有类属性. 通过 dir 可以查到_Employee__company
def __init__(self,name,age):
self.name = name
self.__age = age #私有实例属性
def say_company(self):
print("我的公司是:",Employee.__company) #类内部可以直接访问私有属性
print(self.name,"的年龄是:",self.__age)
self.__work()
def __work(self): #私有实例方法 通过 dir 可以查到_Employee__work
print("工作!好好工作,好好赚钱,娶个媳妇!")
p1 = Employee("高淇",32)
print(p1.name)
print(dir(p1))
p1.say_company()
print(p1._Employee__age) #通过这种方式可以直接访问到私有属性 。通过 dir 可以查到属性:_Employee__age
print(p1.__age) #直接访问私有属性,报错
#p1.__sleep() #直接访问私有方法,报错
结果:
Traceback (most recent call last):
File "C:/Users/housong/PycharmProjects/pythonProject/main.py", line 20, in <module>
print(p1.__age) #直接访问私有属性,报错
AttributeError: 'Employee' object has no attribute '__age'
高淇
['_Employee__age', '_Employee__company', '_Employee__work', '__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__', 'name', 'say_company']
我的公司是: 百战程序员
高淇 的年龄是: 32
工作!好好工作,好好赚钱,娶个媳妇!
32
@property 装饰器
@property可以把一个方法的调用方式变成"属性调用"
#简单测试@property
class Employee:
@property
def salary(self):
return 30000;
emp1 = Employee()
print(emp1.salary) #打印 30000
print(type(emp1.salary)) #打印<class 'int'>
emp1.salary() #报错:TypeError: 'int' object is notcallable
#emp1.salary =1000 #@property 修饰的属性,如果没有加 setter 方法,则为只读属性。此处修改报错:AttributeError: can't set attribute
结果:
Traceback (most recent call last):
File "C:/Users/housong/PycharmProjects/pythonProject/main.py", line 10, in <module>
emp1.salary() #报错:TypeError: 'int' object is notcallable
TypeError: 'int' object is not callable
30000
<class 'int'>
@property 主要用于帮助我们处理属性的读操作、写操作。如果直接读写会不安全,需要通过getter和setter的方法。
class Employee:
'''雇员类,包含姓名和薪水属性'''
def __init__(self,name,salary):
self.name = name
self.__salary = salary
@property
def salary(self):
print("月薪{0},年薪{1}".format(self.__salary,(12*self.__salary)))
return self.__salary;
@salary.setter
def salary(self,salary):
if(0<salary<100000):
self.salary = salary
else:
print("薪水录入有误!限值在0-100000之间")
staff1 = Employee("hoesen",200)
print(staff1.salary)
staff1.salary = -200
结果:
月薪200,年薪2400
200
薪水录入有误!限值在0-100000之间
属性和方法命名总结
方法和属性都遵循下面的规则:
- _xxx:保护成员,不能用“from module import * ”导入,只有类对象和子类对象能访
问这些成员 - xxx:系统定义的特殊成员
- __xxx: 类中的私有成员,只有类对象自己能访问,子类对象也不能访问。(但,在类外
部可以通过“对象名. _类名__xxx”这种特殊方式访问。Python 不存在严格意义的私有成员)
面向对象三大特征
- 封装
- 隐藏对象的属性和实现细节,只对外提供必要的方法。相当于将“细节封装起来”,只
对外暴露“相关调用方法” - 通过“私有属性、私有方法”的方式,实现“封装”。
- 隐藏对象的属性和实现细节,只对外提供必要的方法。相当于将“细节封装起来”,只
- 继承
继承可以让子类具有父类的特性,提高了代码的重用性 - 多态
多态是指同一个方法调用由于对象不同会产生不同的行为
继承
- 继承的语法格式
Python 支持多重继承,一个子类可以继承多个父类。
class 子类类名(父类 1[,父类 2,...]):
类体
- 如果在类定义中没有指定父类,则默认父类是 object 类
- object 是所有类的父
类,里面定义了一些所有类共有的默认实现,比如:new()
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def show_age(self):
print(self.name,"的年龄是",self.age,"\n")
class Student(Person):
def __init__(self,name,age,score):
self.score = score
Person.__init__(self, name, age) #子类的构造函数中一般需要调用父类的构造函数(根据需要,非必须),子类不会自动调用父类的__init__()
s11 = Student("小明",16,99)
print(s11.name,'今年',s11.age,'岁。','期末考试',s11.score,'分。(年龄调用的是属性.age)')
print('下面这个年龄调用的是类中的方法:show_age()')
s11.show_age()
print('s11实例(或者Student类)的所有属性:',dir(s11))
运行结果:
小明 今年 16 岁。 期末考试 99 分。(年龄调用的是属性.age)
下面这个年龄调用的是类中的方法:show_age()
小明 的年龄是 16
s11实例(或者Student类)的所有属性: ['__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__', 'age', 'name', 'score', 'show_age']
类成员的继承和重写
- 成员继承:子类继承了父类除构造方法之外的所有成员
- 方法重写:子类可以重新定义父类中的方法,这样就会覆盖父类的方法,也称为“重写”
下面代码仅在上面代码上新增子类的方法,覆盖掉父类的
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def show_age(self):
print(self.name,"的年龄是",self.age,"\n")
class Student(Person):
def __init__(self,name,age,score):
self.score = score
Person.__init__(self, name, age) #子类的构造函数中一般需要调用父类的构造函数(根据需要,非必须),子类不会自动调用父类的__init__()
def show_age(self):
print(self.name,"的年龄年龄年龄是",self.age,'岁!(这里是,子类重写了show_age()方法)')
s11 = Student("小明",16,99)
print(s11.name,'今年',s11.age,'岁。','期末考试',s11.score,'分。(年龄调用的是属性.age)')
print('下面这个年龄调用的是类中的方法:show_age()')
s11.show_age()
print('s11实例(或者Student类)的所有属性:',dir(s11))
运行结果:
小明 今年 16 岁。 期末考试 99 分。(年龄调用的是属性.age)
下面这个年龄调用的是类中的方法:show_age()
小明 的年龄年龄年龄是 16 岁!(这里是,子类重写了show_age()方法)
s11实例(或者Student类)的所有属性: ['__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__', 'age', 'name', 'score', 'show_age']
类的继承层次查看:mro()或者_mro_
class A:pass
class B(A):pass
class C(B):pass
print('A的继承层次:',A.mro())
print('B的继承层次:',B.mro())
print('C的继承层次:',C.mro())
运行结果:
A的继承层次: [<class '__main__.A'>, <class 'object'>]
B的继承层次: [<class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
C的继承层次: [<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
重写父类的__str__()方法
class A:pass
print(dir(A))
print(A.__str__)
class B(A):
def __str__(self):
return "子类B重写了父类的__str__(),以此留念~"
b = B()
print(b)
运行结果:
['__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__']
<slot wrapper '__str__' of 'object' objects>
子类B重写了父类的__str__(),以此留念~
super() 获取父类的定义
class A:
def say(self):
print("A: ",self)
print("say AAA")
class B(A):
def say(self):
#A.say(self) 调用父类的 say 方法
super().say() #通过 super()调用父类的方法
print("say BBB")
b = B()
b.say()
运行结果:
A: <__main__.B object at 0x00000298FFDD76A0>
say AAA
say BBB
多态
- 多态是指同一个方法调用由于对象不同会产生不同的行为
- 多态是方法的多态,属性不存在多态一说
- 多态的存在有2个必要条件:继承、方法重写
class Animal:
def shout(self):
print("动物叫了一声")
class Dog(Animal):
def shout(self):
print("小狗,汪汪汪")
class Cat(Animal):
def shout(self):
print("小猫,喵喵喵")
def animalShout(a):
if isinstance(a,Animal):
a.shout() #传入的对象不同,shout 方法对应的实际行为也不同。
animalShout(Dog())
animalShout(Cat())
运行结果:
小狗,汪汪汪
小猫,喵喵喵
特殊方法和运算符重载
- python运算符实际上是通过调用对象的特殊方法来实现的
a + b
a.add(b)
常见的特殊方法:
方法 说明 例子
init 构造方法 对象创建:p = Person()
del 析构方法 对象回收
repr,str 打印,转换 print(a)
call 函数调用 a()
getattr 点号运算 a.xxx
setattr 属性赋值 a.xxx = value
getitem 索引运算 a[key]
setitem 索引赋值 a[key]=value
len 长度 len(a)
运算符对应的特殊方法:
运算符 特殊方法 说明
运算符+ add 加法
运算符- sub 减法
<,<=,== lt,le,eq 比较运算符
,>=,!= gt,ge,ne 比较运算符
|,^,& or,xor,and 或、异或、与
<<,>> lshift,rshift 左移、右移
*,/,%,// mul,truediv,mod,
floordiv 乘、浮点除、模运算(取余)、整数除
** pow 指数运算
- 重写上面的特殊方法,即可实现了“运算符的重载”
特殊属性
python对象包含了许多双下划线开始和结束的属性,是特殊属性,有着特殊用法。
常见的特殊属性:
特殊方法 含义
obj.dict 对象的属性字典
obj.class 对象所属的类
class.bases 类的基类元组(多继承)
class.base 类的基类
class.mro 类层次结构
class.subclasses() 子类列表
#测试特殊属性
class A:
pass
class B:
pass
class C(B,A):
def __init__(self,nn):
self.nn = nn
def cc(self):
print("cc")
c = C(3)
print(dir(c))
print(c.__dict__)
print(c.__class__)
print('C.__bases__:',C.__bases__)
print('C.mro():',C.mro())
print('A.__subclasses__():',A.__subclasses__())
print('B.__subclasses__():',B.__subclasses__())
运行结果:
['__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__', 'cc', 'nn']
{'nn': 3}
<class '__main__.C'>
C.__bases__: (<class '__main__.B'>, <class '__main__.A'>)
C.mro(): [<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
A.__subclasses__(): [<class '__main__.C'>]
B.__subclasses__(): [<class '__main__.C'>]
对象的浅拷贝和深拷贝
- 浅拷贝copy.copy():新建一个新的对象,指向源对象的子对象。子对象不会被copy(换汤不换药)
- 深拷贝copy.deepcopy():新建一个新的对象,并且把源对象的子对象也copy下来,新建了新的子对象(另起山头)
import copy
class Moon:
def __init__(self,state):
self.state = state
class Cpu:
def cpu(self):
print('在哪里?',self,'\n')
s = Cpu()
m1 = Moon(s)
m2=copy.copy(m1)
m3=copy.deepcopy(m1)
print('id(m1)?',id(m1))
print('m2浅拷贝了m1,那么id(m2)?',id(m2))
print('m3深拷贝了m1,那么id(m3)?',id(m3),'\n')
print("m1.state.cpu:")
m1.state.cpu()
print("浅拷贝的子对象:m2.state.cpu:")
m2.state.cpu()
print("深拷贝的子对象:m3.state.cpu:")
m3.state.cpu()
id(m1)? 2518656778640
m2浅拷贝了m1,那么id(m2)? 2518687264928
m3深拷贝了m1,那么id(m3)? 2518687265456
m1.state.cpu:
在哪里? <__main__.Cpu object at 0x0000024A6BA486D0>
浅拷贝的子对象:m2.state.cpu:
在哪里? <__main__.Cpu object at 0x0000024A6BA486D0>
深拷贝的子对象:m3.state.cpu:
在哪里? <__main__.Cpu object at 0x0000024A6E7BC760>