day24 类属性与对象属性、init方法、绑定方法【对象绑定、类绑定】、非绑定方法、继承【在子类派生出的新方法中重用父类的两种方式】
今日复习
一、在程序中:
1)先定义类【相同的特征:变量】【相同的技能:函数】的结合体,即类体中最常见的就是变量与函数的定义,但其实类体中是可以存在任意python代码的。
class OldboyStudent:
school = ' oldboy ' =>特征
def choose_cousre(self): => 技能
1)类体代码会在类定义阶段立即执行,会产生一个类的名称空间,用来将类体代码执行过程中产生的名字都丢进去,查看类的名称空间: print ( OldboyStudent._ _dic_ _ )
2)查看类属性: print ( OldboyStudent.school ) # OldboyStudent._ _dic _ _[ ' school ' ]
3) 修改类属性 : OldboyStudent.school = ‘ abc ' # OldboyStudent._ _dic _ _[ ' school ' ] = ‘ abc '
4) 新增类属性: OldboyStudent.country = ‘ usa' # OldboyStudent._ _dic _ _[ ' country ' ] = ‘ usa '
5) 删除类属性: del OldboyStudent.country
结论:定义一个类本质上是开辟了一个类的名称空间或者说是一个存放变量与函数的容器,往这个空间丢数据,作用如下:
1. 用点语法从容器中取出数据来使用。【OldboyStudent.choose_cousre(123) #调用类中函数】
2. 加括号调用类,产生对象
2)调用类的过程称为实例化,返回的结果称之为类的一个对象或一个实例,实例:实际存在的一个例子
3)调用类发生了什么:产生了对应的空的对象名称空间,每一个对象名称空间中都有一个 _ _class_ _ 指向类的名称空间: # stu1. _ _class_ _
stu1 = OldboyStudent() # stu1._ _dic_ _
stu2 = OldboyStudent() # stu2._ _dic_ _
stu3 = OldboyStudent() # stu2._ _dic_ _
4)对象名称空间存放自己的属性,
stu1.name = ' fanny '
stu1.age = 16
stu1. gender = female
======================
stu1.name = ' tom '
stu1.age = 20
stu1. gender = male
问题:以上对象重复代码,需简化
【init 的推导】
-- 为对象简化重复代码方案一:将重复内容定义到一个函数内
def init(obj, x, y, z): def init(obj, name, age,gender):
obj.name = x ===> obj.name = name
obj.age = y obj.name = age
obj.gender = z obj.name = gender
对象调用:
init(stu1, ' fanny ', 16,' female ' )
init(stu2,' tom ', 20,' male ' )
===============================================
-- 简化重复代码方案二: 【可不可以让python 自动调 init函数?】 ==> 可以用下划线开头和下划线结尾就可以自动触发且必须是_ _init _ _ 将函数放入类内部
class OldboyStudent:
school = ' oldboy '
def choose_cousre(self): pass
def _ _ init_ _(self, name, age, gender): # _ _init _ _( stu1, ' fanny ', 16,' female ') 【每一个self都对应着一个对象】
self.name = name
self. age = age
self. gender = gender
stu1 = OldboyStudent()
只要调用类,就会自动调用init函数 ,且报错缺少三个参数,那给它三个参数:TypeError: __init__() missing 3 required positional arguments: 'name', 'age', and 'gender'
stu1 = OldboyStudent( ' fanny ', 16,' female ' )
问题:调用类发生了什么?
1.先产生一个空对象stu1,然后返回
2. 对象产生后触发类中_ _ init _ _ 函数的执行,将对象连同调用类括号内指定的参数一同传入 _ _init _ _ (stu1, ' fanny ', 16,' female ')
stu1 = OldboyStudent( ' fanny ', 16,' female ' ) ==> # _ _init _ _( stu1, ' fanny ', 16,' female ')
stu2 = OldboyStudent( ' tom ', 20,' male ') ==> # _ _init _ _( stu2, ' tom ', 20,' male ' )
总结init的功能:
1.在实例化时就为对象初始自己独有的特征 , 不能有返回值,因为调用类返回一个对象,所以不能有返回值
今日内容:属性查找顺序,init方法、绑定方法、【继承】在子类派生出的新方法中重用父类的两种方式
一、类属性、对象属性查找顺序:【属性分为变量属性&函数属性:attribute】
类中仅存储所有对象共有的内容
对象中存储每个对象独有的内容
查找顺序:先从对象自己的名称空间找没有则去所属的类中找 【类中的的函数不能直接访问类中的变量可以用类名点访问,或者用对象self访问(因为self代表某个对象,对象没有就去类中寻找)】
注: 对象自己内的变量增删改不影响其它对象和类的,而类中定义的变量是所有对象共享的, 对象可以用来用,类也可以来使用,类一旦改变自己的变量属性的值,所有的对象都会随着变
二、init 方法:
2.1 在产生或实例对象后就自定触发init方法,且自动将实例对象作为init的第一个参数传入用于表示对象本身,调用类时括号内的参数为该对象的属性的实参
所以每调用一次类,就会触发一次init函数,init函数中的self就代表该对象。init函数括号内其他的形参就是该对象的初始值。
init函数不允许有返回值,必须为None
三、绑定方法:绑定方法就是函数与谁绑定
对象绑定:就是指类中的函数与对象进行绑定。【类中默认对象绑定】
对象【stu1】调用__init__或choose_coure时,该__init__或choose_coure方法自动将stu1作为self传入。
类调用时,就是一个普通函数,不会自动传参。正常操作传参!
class OldboyStudent:
school = 'oldboy'
def __init__(self, name, age, sex, score=0): => self == stu1 stu1能访问到的属性,self都可以访问到
self.name = name
self.age = age
self.sex = sex
self.score = score
def choose_coure(self): => self == stu1 stu1对象能访问到的属性,self都能访问到
print('%s choosing coures' % self.name)
def eat(self):
print('在%s里吃'% self.school)
stu1 = OldboyStudent('fanny', 16, 'female', 100)
stu1.eat() => 调用eat函数时,对象不用传参,除非eat需要额外的参数
OldboyStudent.eat(OldboyStudent) => 类调用就是一个普通的函数
类绑定:就是指类中的函数与类进行绑定。【@classmethod】
类与对象调用都会自动将类传入 ,self指向了类,类中的属性类与对象都可以访问。通常self用cls来表示
class OldboyStudent:
school = 'oldboy'
def __init__(self, name, age, sex, score=0):
self.name = name
self.age = age
self.sex = sex
self.score = score
def choose_coure(self):
print('%s choosing coures' % self.name)
@classmethod
def eat(self): ==> 此时self == OldboyStudent, 指向了类,类中的属性,对象也可以访问。
print('在%s吃美味 '% self.school)
stu1 = OldboyStudent('fanny', 16, 'female', 100)
stu1.eat()
OldboyStudent.eat()
绑定类的应用:加载配置文件 【这样的好处是:对象不用传参,自动获取类名,类名改了也没有关系】
import settings
class Mysql:
def __init__(self, ip, port):
self.ip = ip
self.port = port
def tell_info(self):
print('<ip:%s port:%s>'%(self.ip, self.port))
@classmethod
def from_conf(cls):
return cls(settings.IP, settings.PORT)
obj2 = Mysql.from_conf()
obj2.tell_info()
非绑定方法:该方法不会自动传参,就是一个普通函数
class OldboyStudent:
school = 'oldboy'
def __init__(self, name, age, sex, score=0):
self.name = name
self.age = age
self.sex = sex
self.score = score
def choose_coure(self):
print('%s choosing coures' % self.name)
@staticmethod
def eat(cls):
print('在%s吃美味 '% cls.school)
stu1 = OldboyStudent('fanny', 16, 'female', 100)
stu1.eat(stu1) # stu1.eat(OldboyStudent) => 此时eat函数就是一个普通函数,正常操作
OldboyStudent.eat(stu1) #OldboyStudent.eat(OldboyStudent) => 此时eat函数就是一个普通函数,正常操作
四、继承 如下有两个类:学生类,老师类,问题:是否可以将两个类中的重复代码抽取出来放在一个公共的类中?
class OldboyStudent:
school = 'oldboy'
def __init__(self, name, age, sex, score=0):
self.name = name
self.age = age
self.sex = sex
self.score = score
def choose_coure(self):
print('%s choosing coures' % self.name)
class OldboyTeacher:
school = 'Oldboy'
def __init__(self, name, age, sex, level):
self.name = name
self.age = age
self.sex = sex
self.level = level
def score(self, stu, num):
stu.score = num
stu1 =OldboyStudent('fanny',17, 'female')
teacher = OldboyTeacher('egon', 20, 'male', 10) => 老师给学生打分
teacher.score(stu1, 100)
答:提取公共部分school以及init放入父类,然后再继承父类中的内容,问题来了:子类中需要自己独有的部分,怎么办?
class OldboyPeople:
school = 'oldboy'
def __init__(self, name, age, sex): => self 被谁触发了,self就是谁,学生对象触发了,self就是学生对象,老师对象发了就是老师对象
self.name = name
self.age = age
self.sex = sex
class OldboyStudent(OldboyPeople):
#school = 'oldboy'
# def __init__(self, name, age, sex, score=0):
# self.name = name
# self.age = age
# self.sex = sex
self.score = score
def choose_coure(self):
print('%s choosing coures' % self.name)
class OldboyTeacher(OldboyPeople):
#school = 'Oldboy'
# def __init__(self, name, age, sex, level):
# self.name = name
# self.age = age
# self.sex = sex
# self.level = level
def score(self, stu, num):
stu.score = num
stu1 =OldboyStudent('fanny',17, 'female') => 子类中没有init就去父类中找,但不能传成绩,父类中的init函数只需要3个参数
print(stu1.__dict__) #{'name': 'fanny', 'age': 17, 'sex': 'female'}
tea1 = OldboyTeacher('egon', 18, 'male') => 子类中没有init就去父类中找,但不能传等级,父类init只需要3个参数
print(tea1.__dict__) #{'name': 'egon', 'age': 18, 'sex': 'male'}
答:在子类派生出的新方法中重用父类功能的两种方式:
1.重用父类的第一种方式:指名道姓地引用一个类中函数:OldboyPeople.__init__(self,name, age,sex) ,指名道姓就像在一个函数内调用另一个函数,
结论:不需要继关系,没有自动传值的功能【因为是类调在用】
class OldboyPeople:
school = 'oldboy'
def __init__(self, name, age, sex):
self.name = name,
self.age = age
self.sex = sex
class OldboyStudent:
school = 'oldboy'
def __init__(self, name, age, sex, score=0):
OldboyPeople.__init__(self,name, age,sex) =>类调用函数是没有自动传值一说,只是单纯的需要四个参数,self此时就是凋用者stu1
self.score = score
def choose_coure(self):
print('%s choosing coures' % self.name)
class OldboyTeacher:
school = 'Oldboy'
def __init__(self, name, age, sex, level):
OldboyPeople.__init__(self,name,age, sex) => 类调用函数是没有自动传值一说,此时self就是调用者tea1
self.level = level
def score(self, stu, num):
stu.score = num
stu1 =OldboyStudent('fanny',17, 'female')
print(stu1.__dict__) #{'name': 'fanny', 'age': 17, 'sex': 'female', 'score': 0}
tea1 = OldboyTeacher('egon', 18, 'male',100)
print(tea1.__dict__) # {'name': 'egon', 'age': 18, 'sex': 'male', 'level': 100}
2. 重用父类的第二种方式:super() 该方法必须在类中用
-- 在python2中:super(自己的类名, 【类自已的对象】self)
-- 在python3中: super() ,py3中可以不用写参数
调用 super()该函数会得到一个特殊的对象,该对象专门用来访问父类中的属性,查找顺序完全参照mro列表!
结论:1)严格依赖继承的mro列表
2)既然是对象访问,就有自动传值的效果,想象是对象调用函数
class OldboyPeople:
school = 'oldboy'
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
class OldboyStudent(OldboyPeople):
school = 'oldboy'
def __init__(self, name, age, sex, score=0):
# OldboyPeople.__init__(self,name, age,sex)
super(OldboyStudent,self).__init__(name, age, sex) => 括号中py3中可写可不写,super()得到的是一个对象,对象调用方法有自动传参效果,所以父类的self不用再写了
self.score = score
def choose_coure(self):
print('%s choosing coures' % self.name)
class OldboyTeacher(OldboyPeople):
school = 'Oldboy'
def __init__(self, name, age, sex, level):
super().__init__(name,age, sex) => 同上,super()括号中的参数可写可不写
# OldboyPeople.__init__(self,name,age, sex)
self.level = level
def score(self, stu, num):
stu.score = num
stu1 =OldboyStudent('fanny',17, 'female')
stu1.score =60
print(stu1.__dict__)
tea1 = OldboyTeacher('egon', 18, 'male',100)
print(tea1.__dict__)
完全参照mro列表举例:
class A:
def f1(self):
print('A.f1')
super().f2() ==> 此时super() 就是mro列表中的B类,【由谁引发了属性查找,无任在哪,就依照谁的mro列表查找*****】
class B:
def f2(self):
print('B.f2')
class C(A,B):
def f2(self):
print('C.f2')
obj = C()
print(C.mro()) # [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
obj.f1() => 先去C类找-> A类 -> B类
A.f1
B.f2