阅读目录
内容回顾
类属性与对象属性
初始化方法init
绑定方法
对象之间交互练习
OOP三大特性之继承
抽象与继承
一切皆为对象
存在继承关系后的属性查找
派生与覆盖
子类中重用父类的方法
##内容回顾
#1. xml模块 解析xml格式的文档 可扩展的标记语言"123" >text 1. tree = ElementTree.parser("文件路径") root = tree.getroot() root.iter/find/findall 直接遍历某标签 取出所有子级 element.text = "" element.tag = "xxx" element.set("k","v") 要删除需要先找父级 父级.remove(子标签对象) 父级.append(子标签) tree.write("路径") #2.面向对象的基本概念 是一种编程思想, 优点:扩展性高,复用性强,可维护性好 缺点:编程复杂度提高了,需要先设计,结果无法准确预知 使用场景:对扩展性要求高的程序 面向过程: 优点:复杂问题简单化,流程化 缺点:扩展性极低,牵一发动全身,维护性相比OOP低 使用场景:对扩展性要求低的程序 将程序员从一个具体的操作者转变为指挥者 #3.类与对象的关系 对象是 具备某些特征和行为的结合体,是具体存在的某个事物 类 某些具备相同特征和相同行为的集合体,是一种抽象概念 创建类的语法 class 大驼峰命名: 属性用变量标识 (数据属性) 名称=值 行为用函数表示 (函数属性/方法) def 函数名称 创建对象 类名加括号 Person() #4.属性的访问顺序 优先访问对象自己的 如果没有 再访问类中的
##类属性与对象属性
#1类中应该仅存储所有对象共有的内容 如所有人的国籍相同那就放到类中 #2、对象中存储每个对象独有的内容 如每个人的名字都不同 #示例 # class Person: # color = "black" # # def eat(self): # print("黑人哥们正在吃饭....") # 创建对象 也称之为实例化 产生的对象也称之为实例 # p1 = Person() # print(p1) # print(p1.color) # print(p1.__dict__) # # p2 = Person() # print(p2) # print(p2.color) # print(p2.__dict__) # # # print(id(p1.color)) # print(id(p2.color)) # print(id(Person.color)) # 当修改某一个对象的属性时 不会影响其他对象和类 # p2.color = "yellow" # print(p2.color) # print(p1.color) # print(Person.color) # # Person.color = "red" # print(p1.color) # print(p2.color) # 类中应该仅存储 所有对象都相同的内容 # 如果每个对象的属性都不同则应该放到对象自己的名称空间中 # 如一个班级中所有学员的老师都相同 class Student: school = "oldboy" def study(self): print("正在学习......") # stu1 = Student() # stu1.name = "张无忌" # stu1.age = 17 # stu2 = Student() # stu2.name = "张翠山" # stu2.age = 37 # 封装一个方法来初始化对象的属性 # def my_init(stu,name,age): # stu.name = name # stu.age = age # # stu1 = Student() # stu2 = Student() # # my_init(stu1,"张无忌",17) # my_init(stu2,"张翠山",37) # # print(stu1.name) # print(stu2.name)
##初始化方法init
# 什么是初始化方法 -----用于为对象的属性设置初始值的函数 # 为什么需要初始化方法 -----在类的实例(对象)中,一些属性是必须存在的,就可以使用初始化函数来完成,比如`Student`对象中的`name`属性,它是必须的,用于唯一标识一个学生 #执行过程: -----在创建对象时`Student("jack")`会申请新的内存空间用于保存对象数据,接着**自动调init函数 #注意: -----`__init__`函数要求第一个参数必须是self,该参数表示需要被初始化的对象本身,这样就可以将name属性绑定到对象上 -----可以将self改为其他任意的名称,但为了保证易读性通常是self,额外的参数须位于self之后 -----有了`__init__`方法,在创建实例的时候,就不能传入空的参数了,必须传入与`__init__`方法匹配的参数,但`self`不需要传,Python解释器自己会把实例变量传进去: #示例一: class Student: def __init__ (self,name): print("init run") self.name = name # stu1 = Student() # 以上代码将抛出异常:TypeError: __init__() missing 1 required positional argument: 'name' stu1 = Student("jack") # 输出 init run print(stu1.name) # 输出 jack #示例二 class Dog: def __init__(self,age,name,**kwargs): # print("init run") # print(self) self.age = age self.name = name # d = Dog() # === Dog.__init__(d) # # print(Dog.__init__) # # print(d) # # print(d.age) # # # Dog() d1 = Dog(1,"大花") d2 = Dog(2,"校花") print(d1.name) print(d2.name) #总结 # 执行时机:当实例化产生对象时会自动执行该函数 # 会自动传入需要初始化的对象 # 初始化必须包含至少一个参数 用于表示对象本身 # 该函数不允许有返回值 必须为None
##绑定方法
# 什么是方法? 先理清方法,函数,技能的关系: 生活中对象的技能在程序中用函数表示 函数在面向对象中称之为方法,换种称呼而已! 如此说来,绑定方法也就是绑定函数 # 为什么要绑定? 在使用面向对象之前,数据与处理数据的函数是独立的没有任何联系,在调用函数时需要手动传入参数,如果要处理的数据有很多,参数的传递就是一个非常麻烦的事情, 原始的处理方式:函数 传参 问题1 调用函数时传入参数,如果要处理的数据有很多,编写了很多重复的代码,代码的阅读性很差 问题2 后期如果每次处理的数据个数变多了,函数需要修改参数列表,导致以前写的所有代码都需要修改,扩展性非常差 问题3 如果传入了错误的数据,比如需要整型却传入了字符串,造成函数无法正常工作 绑定方法的处理方式: 1.调用方法时传入对象,对象中包含了需要的所有数据,减少重复代码 2.后期数据变化时,修改类对象中的属性,方法中增加相应的处理代码,而方法参数不会发生变化,提高了扩展性 3.方法与对象进行绑定,没有对象则无法使用方法,并且在创建对象的初始化方法中,已经确定了各个属性数据时正确的,如此一来避免了传入使用错误数据执行函数造成的问题 简单的说,就是将数据与处理数据的函数绑定在一起,没有数据则根本不需要处理数据的函数,反过来要执行处理数据的函数则必须提供要被处理的数据 # 绑定方法与普通函数的区别 1、当使用类调用时,就是一个普通函数 有几个参数就得传几个参数 2、当用对象来调用时,是一个绑定方法了,会自动将对象作为第一个参数传入 # 类中定义的函数分成两大类 #1:绑定方法 1.绑定到对象的方法:没有被任何装饰器装饰的方法。 在类中定义的函数默认都是绑定到对象的方法 特点:参数的第一个必须是self 表示当前对象本身,使用对象来调用,调用时会自动传入对象 2.绑定到类的方法:用classmethod装饰器装饰的方法。 特点:参数的第一个必须是cls表示当前类本身,使用类名来调用,调用时会自动传入类 #2:非绑定方法:用staticmethod装饰器装饰的方法 特点:不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。就是一个普通函数 不过由于作用域在类中所以需要使用类或对象类调用 #绑定到类的方法与绑定到对象的方法总结 #异同点: 相同 绑定对象调用时都有自动传参的效果 绑定到谁给谁就由谁来调用 不同 绑定到类的方法自动传入当前类 绑定到对象的方法自动传入当前对象 #另外 绑定方法中的self 和 cls参数名 是可以随意修改的,但是self和cls是约定俗成的写法,为了提高可读性不建议修改
#示例一 class Person: def __init__(self,name,age): self.name = name self.age = age # 默认情况下 在类中定义的方法 都是绑定方法 def say_hi(self): print("hello i am %s" % self.name) # 当你创建对象时 发生什么 # 1.产生一个空对象 (名称空间) # 2.自动调用__init__方法 并且把这个对象以及额外的参数传入 # p = Person("赵敏",17) # Person.__init__(p) # p.say_hi() # # p2 = Person("谢逊",50) # p2.say_hi() # 经常使用的数据定义为变量 username = "jack" pwd = "123" db_name = "qq" atm_usr = "jerry" atm_pwd = "123" #用于登录数据库 def login_data_base(username,pwd,db_name): print("%s登录%s数据库 密码为%s" % (username,db_name,pwd)) def login_atm(usr,pwd): print("%s 登录了ATM 密码为%s" % (usr,pwd)) login_data_base(username,pwd,db_name) login_data_base(username,pwd,db_name) login_atm(atm_usr,atm_pwd) login_atm(atm_usr,atm_pwd) #示例二 class Student: school = "oldboy" def __init__(self,name): self.name = name # 默认情况下是绑定方法 def study(self,a): print(self) print(a) # print("%s正在学习" % self.name) # print(Student.study) s = Student("jack") # print(s.study) s.study(100) # 对于类而言study就是普通函数 # Student.study(1) # 而对于对象而言 他是一个绑定方法 当使用对象来调用时 会自动将对象作为第一个参数传入 s1 = Student("rose") print(id(s1.study)) print(id(s.study)) print(s1.study) print(s.study) # print(s.school) # print(s1.school) #示例三 import time class Person: country = "china" # init 也是绑定方法 def __init__(self,name,age): self.name = name self.age = age @classmethod def info(cls): print(cls) print("info run") def sey_hi(self): print("hello i am %s" % self.name) # 是要输出这个类的一些信息 @classmethod def class_info(cls): print("this class %s is module:xxx.py" % cls.__name__) # 输出当前时间 # 不需要访问类也不需要访问对象 所以直接做成非绑定方法 @staticmethod def show_time(self): print(time.localtime()) # p = Person("rose",20) # p.info() # # print(Person) # print(Person.info) # Person.info() # p = Person("rose",10) # p.sey_hi() # p = Person("周芷若",20) # # Person.class_info(p) # Person.class_info() p = Person("rose",1) p.show_time(1) Person.show_time(2) # # print(Person.show_time) # print(p.show_time) #示例三 ''' ### 练习 1.创建Student类 2.拥有以下属性: 姓名 性别 年龄 学校 班级 3.拥有以下方法 save(name) 其作用是将这个对象序列化到文件中 get_obj(name) 其作用是根据name从文件中反序列化为得到一个对象 分析save方法和get_obj 应该作为绑定给对象还是绑定给类 ''' import json,os class Student: school = "oldboy" def __init__(self,name,gender,age,cls_name): self.name = name self.gender = gender self.age = age self.cls_name = cls_name # 将对象的数据序列化到文件中 def save(self): with open(self.name,"wt",encoding="utf-8") as f: json.dump(self.__dict__,f) @staticmethod def get_obj(name): if not os.path.exists(name): print("不存在该对象!") return None with open(name,"rt",encoding="utf-8") as f: dic = json.load(f) stu = Student(**dic) return stu # stu = Student("jack","woman",20,"py8") # stu.save() # stu = Student("lucy","woman",20,"py8") # stu.save() stu = Student.get_obj("jason") # print(stu.cls_name)
#对象之间交互练习
""" 需求设计王者荣耀中的英雄类,每个英雄对象可以对其他英雄对象使用技能 具备以下属性 英雄名称,等级,血量 和Q_hurt,W_hurt,E_hurt 三个属性,表示各技能的伤害量 具备以下技能 Q W E 三个技能都需要一个敌方英雄作为参数,当敌方血量小于等于0时角色死亡 涉及到英雄对象 属性: 名字 等级 血量 行为: Q W E 需要一个英雄类 """ class Hero: def __init__(self,name,level,HP,Q_hurt,W_hurt,E_hurt): self.name = name self.level = level self.HP = HP self.Q_hurt = Q_hurt self.W_hurt = W_hurt self.E_hurt = E_hurt def Q(self,enemy): print("%s 对 %s 释放了 Q技能 造成了%s伤害" % (self.name,enemy.name,self.Q_hurt)) self.attack(enemy, self.Q_hurt) def W(self,enemy): print("%s 对 %s 释放了 W技能 造成了%s伤害" % (self.name, enemy.name, self.W_hurt)) self.attack(enemy, self.W_hurt) def E(self,enemy): print("%s 对 %s 释放了 E技能 造成了%s伤害" % (self.name, enemy.name, self.E_hurt)) self.attack(enemy,self.E_hurt) def attack(self,enemy,hurt): enemy.HP -= hurt if enemy.HP <= 0: print("%s 已被%s击杀" % (enemy.name, self.name)) # 创建两个英雄对象 arso = Hero("亚瑟","15",1000,30,500,200) dj = Hero("妲己","15",800,300,10,800) # # dj.W(arso) # dj.Q(arso) # dj.E(arso) arso.Q(dj) arso.W(dj) arso.E(dj) dj.W(arso) dj.Q(arso) dj.E(arso)
##OOP三大特性之继承
# 1.什么是继承 继承是一种关系,通过继承关系,一个对象可以直接使用另一个对象拥有的内容,例如王思聪继承王建林,王思聪就可以使用王健林拥有的财产! 被继承的一方称之为父,即王健林; 继承的一方称之为子,即王思聪 OOP继承描述的是两个类之间的关系,通过继承,一个类可以直接使用另一个类中已定义的方法和属性; 被继承的称之为父类或基类,继承父类的类称之为子类; #2.为什么需要继承 极大的提高了代码的复用性 #3.使用继承: 至少需要两个类 语法: class 子类名称(父类名称): pass #示例1 # class Teacher: # school = "oldboy" # def __init__(self,name,age): # self.name = name # self.age = age # # def say_hi(self): # print("hello i am %s" % self.name) # def teach(self): # print("正在教书......") # # class Student(Teacher): # pass # # print(Student.school) # print(Student.say_hi) # print(Teacher.say_hi) # # s = Student("rose","123") # s.say_hi() # # # 在上述案例中通过继承 学生就拥有了老师的所有内容 但是学生不应该教书这个技能 # 意味着这个继承关系 有问题 不合理 # 需要先抽象 在继承
##抽象与继承
#抽象与继承 继承之后可以直接使用父类的属性和方法 使用继承时 应该先抽象 在继承 抽象指的是 将一系列类中相同的特征和相同行为抽取 形成一个新的类 会产生一些与原本业务不想关的类 站在不同角度会得到不同的抽象结果
应当将`Teacher`与`Student`中完全相同的部分抽取出来,放到另一个类中,
并让`Teacher与Student`去继承它,这个类称之为`公共父类` ,但是这个类与
实际的业务需求是无关的在现实中也不实际存在,它的作用仅仅是存储相同代
码以减少重复;这一过程我们称之为抽象。
#综上所述,正确思路是:先抽象在继承
#示例1 # 抽象得到的公共父类 class OldBoyPerson: school = "oldboy" def __init__(self,name,age): self.name = name self.age = age def say_hi(self): print("hello i am %s" % self.name) class Teacher(OldBoyPerson): def teach(self): print("正在教书......") class Student(OldBoyPerson): pass # 测试 t = Teacher("owen",38) t.say_hi() t.teach() s = Student("歌王",20) s.say_hi()
##一切皆为对象
#在python3中任何类都直接或间接继承自Object class Test: pass a = 10 print(type(a)) li = [] print(type(li)) t = Test() print(type(t)) # a = 10 # a = int(10) # # a = int li = [] # print(li) # li.append(10) # print(li) list.append(li,10) print(li) t = (1,2,3) print(type(t)) # 一切皆对象指的是 在python中您所使用到的任何数据都是对象 int float list dict 模块 包 # import test1 # print(type(test1)) # import package # print(type(package)) def t(): pass # class a: # pass # print(type(t)) # print(type(a))
##存在继承关系后的属性查找
#一个类必然继承另一个类,被继承的类也有可能继承了其他类,相当于C继承B,B又继承A #此时查找属性的顺序是: 对象本身的名称空间 - > 类的名称空间 -> 父类的名称空间 -> 父类的父类名称空间 ->object类 #会沿着继承关系一直往后查找,直到找到为止,由于object是所有类的根类,所以如果找不着最后都会查找object类! #示例 class A: name = "scot" # def __str__(self): # print("111111111") # pass pass class B(A): name = "rose" pass b = B() # b.name = "jack" print(b.name) # print(b.__str__()) # 查找顺序 # 对象自己 - > 所在的类 -> 所在类的父类 -> 父类的父类 -> object
##派生与覆盖
# 什么是派生 当父类提供的属性无法完全满足子类的需求时,子类可以增加自己的属性或非法,或者覆盖父类已经存在的属性,此时子类称之为父类的派生类; # 什么是覆盖 在子类中如果出现于父类相同的属性名称时,根据查找顺序,优先使用子类中的属性,这种行为也称为`覆盖` 从`Person`类派生出来的`Teacher`类 #示例 """ #派生 当一个类继承自另一个类 并且这个子类拥有与父类不同的内容 就称之为派生 """ # class A: # def show_info(self): # print("hello world!") # # class B(A): # pass """ #覆盖 (重写) 子类中出现了与父类名称相同的属性 或方法 就会覆盖掉父类的属性或方法 """ class A: text = "123" def show_info(self): print("hello world!") class B(A): text = "321" def show_info(self): print("hello 你是DSB!") pass b = B() b.show_info() print(b.text)
#示例2
# 抽取老师和学生的相同内容 形成一个新的类,作为它们的公共父类
class Person:
def __init__(self,name,gender,age):
self.name = name
self.gender = gender
self.age = age
def say_hi(self):
print("my name is %s age is %s gender is %s" % (self.name,self.age,self.gender))
class Teacher(Person): #指定Teacher类继承Person类
# Teacher类从Person类中继承到了say_hi方法 但是,老师打招呼时应当说出自己的职业是老师,所以需要
# 定义自己的不同的实现方式
def say_hi(self):
print("hi i am a Teacher")
#print("my name is %s age is %s gender is %s" % (self.name,self.age,self.gender))
#上一行代码与父类中完全相同,可以直接调用父类提供的方法
Person.say_hi(self)
# 创建Teacher对象
t1 = Teacher("Jack","man",20)
t1.say_hi()
#输出 hi i am a Teacher
# my name is Jack age is 20 gender is man
#注
在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该使用调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,因此即便是self参数也要为其传值
##子类中重用父类的方法
很多情况下 子类中的代码与父类中仅有小部分不同,却不得不在子类定义新的方法,这时候可以在子类中调用父类已有的方法,来完成大部分工作,子类仅需编写一小部分与父类不同的代码即可 在子类中有两种方式可以重用父类中的代码 1.使用类名直接调用 ,该方式与继承没有关系,即时没有继承关系,也可以调用 2.使用super() #示例一 class Person: text = "321" def __init__(self,name,age,gender): self.name = name self.age = age self.gender = gender def sleep(self): print("人类 午睡 躺着睡!") def say_hi(self): print("my name :%s my age :%s my gender: %s " % (self.name,self.age,self.gender),end="") class Student(Person): text = "123" def __init__(self,name,age,gender,number): #======================================================================重点在这里 # 由于父类已经存在一个方法可以完成这个三参数的初始化 # 所以可以直接调用父类的初始化完成这部分的初始化工作 # 方法1 # Person.__init__(self,name,age,gender) # 指名道姓的调用 # 方法2 在py2中不支持 super().__init__(name,age,gender) # py2的写法 # super(Student, self).__init__(name,age,gender) self.number = number #====================================================================== # 访问父类的属性 def show_text(self): print(self.text) print(super().text) def say_hi(self): super().say_hi() print("my number: %s" % self.number) # print("my name :%s my age :%s my gender: %s my number: %s" % (self.name, self.age, self.gender,self.number)) s = Student("jack",20,"man","007") s.say_hi() # s.show_text() #示例二 class Vehicle: #定义交通工具类 Country='China' def __init__(self,name,speed,load,power): self.name=name self.speed=speed self.load=load self.power=power def run(self): print('开动啦...') class Subway(Vehicle): #地铁 def __init__(self,name,speed,load,power,line): #super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self) super().__init__(name,speed,load,power) self.line=line def run(self): print('地铁%s号线欢迎您' %self.line) super(Subway,self).run() class Mobike(Vehicle):#摩拜单车 pass line13=Subway('中国地铁','180m/s','1000人/箱','电',13) line13.run()