面向对象的三大特征
'''
面向过程是对简单任务的处理,着重于解决的手段 是执行者的思维
面向对象是处理复杂、大型任务以及需要协作的任务的方法,将大型任务拆解程小部分,再由面向过程来处理 是设计者的思维
python面向对象的三大特征:
1.封装
2.继承
3.多态
'''
'''
类相当于一个模具,所有定义在类中的函数,都已经包含了类的属性
类里面除了存放属性如年龄、姓名等内容,还会储存方法,方法存储在值中
函数定义在类里就叫方法
'''
initial类的实例属性初始化
class Student: #构造类 class声明
def __init__(self,name,score): #initial初始化类,括号内第一个必须是self,给类赋值
self.name=name #转化为对象的属性,再次调用必须用self进行调用
self.score=score
#可以通过self进行调用 self后面是定义的实例属性名
#当需要构造一个类中的对象时,init规定了需要传入的实际参数种类和个数,是构造的方法
def say_score(self):
self.age=18 #新增一个age的实例属性
print("姓名:{0} 成绩:{1}".format(self.name,self.score))
s1 = Student("Jim",85)
s1.say_score()
#Student.say_score(s1) 这是解释器翻译出来的语句
s1.salary=3000
#以上只会对s1这个对象进行修改新创建一个s2不会包含这两项,因为模具没有改变
print(s1.__dict__)
#将s1作为字典输出,s1是一个对象,在类中
s2 = Student("Jack",89)
#print(s2.age) age新增的语句是在say_score中,虽然方法是在模具中但如果不调用不会添加属性
dir(s1) #分析内存调用
print(isinstance(s1,Student)) #判断是否属于哪个类 isinstance(类对象,类名)
call方法——可以像函数那样调用实例属性
class SalaryAccount:
def __call__(self, salary):
print(salary*12)
return salary*12
#call函数是可以直接调用输入的实例对象的函数,可以直接计算,不需要init初始化
s=SalaryAccount() #创建实例对象,与call无关,只与初始化init有关
'''
本质上类作为一个对象之所以不可被直接 s()进行调用是因为缺少call函数
def f1():
pass
f1()
f1()之所以可以直接调用运行正是因为包含了call函数,它定义了他被调用时的动作
'''
s(3000) #定义了call以后,实例对象可以被看作函数来执行
'''
init定义的初始化是要求创建实例对象时的输入
call是定义了作为可调用对象调用时的输入和程序
'''
类属性
class Members:
company="996" #类属性,直接定义在类里面,只会存在在Members的堆内存中,新建的实例对象不会保存一份,而是保留能指向这些属性的指针
count=0
def __init__(self,name,age):
self.name=name #实例属性,输入以后才会有,每个实例对象中保存的是实例属性
self.age=age
Members.count=Members.count+1 #每创建一个对象都会+1,可以计算创建对象的个数
def say(self): #类方法,实例对象中的方法会指向类中定义的代码
print(self,self.name,self.age)
s1=Members("Jim",22)
s1.say()
print(s1.count) #可以调用,但是只存在在类中,独一份
方法的动态性
#Python中方法没有重载,同名方法以后来者为准
class Students:
def print(self):
print("1")
def printout(s): #一定要有形参,即便没有用,因为类方法中需要传入self,需要给他留位置
print("2")
def print2(s): #再次新定义一个函数
print("3")
#方法的添加
Students.printout=printout #这个语句本质上是新建一个类方法指向同一个函数对象
Students.print=print2 #将原先的类方法指向新的函数对象
s=Students()
s.printout()
s.print()
类方法、静态方法
class Members:
company="996" #类属性,直接定义在类里面,等于每个使用磨具的都会被刻上印子
count=0
def __init__(self,name,age):
self.name=name #实例属性,输入以后才会有
self.age=age
Members.count=Members.count+1 #每创建一个对象都会+1,可以计算创建对象的个数
def say_information(self): #实例方法,方法跟随模具,但是需要调用才会启动
print("company:",Members.company)
print("姓名:{0} 年龄:{1}".format(self.name,self.age))
#实例方法将代码信息存在类里面,每一个新建的类对象都会包含这一个方法
@classmethod #声明类方法,他不会刻在新建的类对象中,仅仅只储存在类模具里,但与类对象有关
def printout(cls):
print("over")
print(cls.count) #只能调用类属性,用class开头进行调用
#需要用类名.类方法()进行调用
# 不可以调用实例属性和实例方法,因为从属关系不同,类方法只存储在类中
@staticmethod #他只是存储在类对象中,但是与类无关
def none(a,b):
return a+b
#静态方法也同样只可以调用类属性
s1=Members("jim",22)
s1.say_information()
print(s1.count)
s1.printout()
print(s1.none(1,2))
#之所以实例对象的内存空间并没有储存类属性和静态方法,但它也可以进行调用,这是因为它储存了能指向这些的指针用于引用
私有属性
class Students:
__school="SHU"
def __init__(self,name,age):
self.name=name
self.__age=age #私有属性定义是加两个下划线
def __print(self):
print("1")
print(Students.__school) #类内部可以直接访问私有属性
s=Students("Jim",18)
print(s.name)
print(s._Students__age) #外部不能直接访问私有属性,可以通过 _类名__私有属性名来强制访问
s._Students__print() #私有方法也是属性,也同样需要这样访问
property——可以应用于外部对私有属性的访问和修改
class Employee:
def __init__(self,name,salary):
self.name = name
self.__salary = salary
@property
def salary(self): #常用于私密属性的访问,给出一个函数,property后可以以属性形式读取
print("salary printing...")
print(self.__salary)
#仅property后面跟的方法会成为property
@salary.setter
def salary(self,salary): #给出一个可以外部更改私密属性的方法,直接属性名赋值即可
if salary >= 10000:
self.__salary = salary
s1=Employee("Jim",12000)
s1.salary
s1.salary = 20000 #调用setter重新赋值
s1.salary
'''
property可以把方法转换成为属性,调用直接输出,而原本调用需要有输入形参的(),他是函数
'''
重写str方法——本质上是转字符串类型的返回值修改
class Person:
def __init__(self,name):
self.name=name
def __str__(self):
'''将对象转换成一个字符串描述'''
print("重写str方法")
return "名字是{0}".format(self.name)
s=Person("Jim")
print(s) #本来返回的是s的信息
a=str(s) #调用__str__方法,并将return的值返回给a
'''
重写str方法
名字是Jim
重写str方法
print和str()本质上都是调用了类中的__str__方法,他重新定义了返回值
'''
运算符的重新定义
#运算符本质上是调用类中的方法
class Person:
def __init__(self,name):
self.name=name
def __add__(self,other): #a+b 在解释器中执行是 a.__add__(b),所以进来的是b这个对象,other也是指要加的这个对象
if isinstance(other,Person): #判断对象是否是在person类里,只有相同类的实例对象才可以公用方法
return "{0}-{1}".format(self.name,other.name) #如果是同类型对象那么init定义也是相同的方法
else:
return "不是同类型对象"
p1=Person("Jim")
p2=Person("Jack")
p3="ame"
print(p1+p2) #p1.__add__(p2)
print(p1+p3) #会返回不是同类型 因为p3不属于Person类
类的继承
#继承是面向对象的一大特点,使得类有更高的扩展性,也降低了重复工作
class Person:
def __init__(self,name,age):
self.name=name
self.__age=age
@property
def say_age(self):
return self.__age
class Student(Person): #可以继承多个类,没有特别声明,父类即为object类
def __init__(self,name,age,score):
#Person.__init__(self,name,age) #调用父类的初始化,给解释器明确关系,也可以用super调用
super(Student,self).__init__(name,age)
self.score=score
'''
子类继承除了构造方法以外的一切成员
当子类中没有重写init时,会默认使用父类的初始化
'''
s=Student("Jim",22,85)
print(s.say_age)
#子类里面没有东西,但是继承的父类里有方法,可以调用
print(s.name)
print(s.score)
super()调用父类方法
#super()获得的仅仅是父类定义而非父类对象
class A:
def say(self):
print("aa")
class B(A):
def say(self):
super(B,self).say() #super(子类名,self).方法名([参数])
#A.say(self) 也可以获得父类定义 父类名.函数名(self,[参数])
print("bb")
B().say()
继承与子类方法的重写
class Person:
def __init__(self,name,age):
self.name=name
self.__age=age
def say_name(self):
print(self.name)
def say_age(self):
print(self.__age)
class Student(Person):
def __init__(self,name,age,score):
Person.__init__(self,name,age) #调用父类的初始化,给解释器明确关系
self.score=score
def say_name(self): #子类方法重写父类方法后,子类中不会再调用父类方法,父类方法不会被改变
print("My name is {0}".format(self.name))
#子类里面的同名方法已经重写
s1=Student("Jack",23,90)
s1.say_name()
s1.say_age() #子类继承可以调用父类
#父类不会被改变
print(Student.mro()) #查看类的层次结构
print(dir(s1)) #查看结构属性,方法也是一种属性
多态——建立在继承的基础上
#对于同一个方法,不同的子类有不同的运算
#多态必须存在继承和方法的重写
class man:
def eating(self):
print("eating")
class chinese(man):
def eating(self):
print("with chopstick")
class indian(man):
def eating(self):
print("with hands")
class english(man):
def eating(self):
print("with folk & knife")
def man_eating(m): #传入一个对象
if isinstance(m,man): #isinstance()可以查找该对象是否在后面的类中,子类存在父类里
m.eating() #对象.eating() 调用不同子类里的方法
else:
print("Error")
man_eating(indian()) #用对象来调用
组合
class A1:
def say_a1(self):
print("A1,A1")
class B1(A1): #继承是子母类关系,子类可以调用母类的方法不需要重新定义
pass
b1=B1()
b1.say_a1()
print(">>>>>>组合测试")
class A2:
def say_a2(self):
print("A2,A2")
class B2:
def __init__(self,c): #组合是通过将另一个类创建的对象在新的类中初始化
self.inner=c
#组合可以使得实例对象能够递归调用其他类的方法
a2=A2()
b2=B2(a2)
b2.inner.say_a2()
'''
组合的本质是将具有特殊方法的类作为自己的属性进行初始化,通过 实例对象.属性.特殊方法() 可以调用其他类中的方法
形成代码复用
'''
深拷贝、浅拷贝
import copy
class Person:
def __init__(self,hands,feet):
self.hands = hands
self.feet = feet
pass
class hands:
pass
class feet:
pass
h1=hands()
f1=feet()
P1=Person(h1,f1) #feet 和 hands是导入的两个子对象,子对象是根据另外两个类创建
P2=copy.copy(P1) #浅拷贝
print(P1,P1.hands,P1.feet)
print(P2,P2.hands,P2.feet)
'''
<__main__.Person object at 0x0000018EE8CE3310> <__main__.hands object at 0x0000018EE8CE3B80> <__main__.feet object at 0x0000018EE8CE3730>
<__main__.Person object at 0x0000018EE8D3A110> <__main__.hands object at 0x0000018EE8CE3B80> <__main__.feet object at 0x0000018EE8CE3730>
可以看到浅拷贝是创建了一个新的对象指向同样的子对象,不会全部复制一份,只拷贝源对象
'''
print(">>>>>>>测试深拷贝")
P3=copy.deepcopy(P1)
print(P1,P1.hands,P1.feet)
print(P3,P3.hands,P3.feet)
'''
<__main__.Person object at 0x000002745E89BEB0> <__main__.hands object at 0x000002745E89BFD0> <__main__.feet object at 0x000002745E89BEE0>
<__main__.Person object at 0x000002745E899E10> <__main__.hands object at 0x000002745E899CF0> <__main__.feet object at 0x000002745E899C90>
可以看到深拷贝会全盘复制所有的子对象,递归拷贝全部的子对象
'''
类的特殊属性
class A:
pass
class B:
pass
class C(B,A):
pass
c=C()
print(C.mro()) #类的层次结构
print(C.__base__) #父类
print(C.__bases__) #直接父类的元组,多重继承
print(c.__class__) #所属类的信息