Python Day 24 类属性与对象属性、初始化方法init、绑定方法与非绑定方法、OOP三大特性之继承、抽象与继承、存在继承关系后的属性查找、派生与覆盖、子类中重用父类的方法

  阅读目录

    内容回顾

    类属性与对象属性

    初始化方法init

    绑定方法

    对象之间交互练习

    OOP三大特性之继承

    抽象与继承

    一切皆为对象

    存在继承关系后的属性查找

    派生与覆盖

    子类中重用父类的方法

  ##内容回顾

#1. xml模块
    解析xml格式的文档
    可扩展的标记语言
    <tag name="123" >text</tag>

    <tag1>
        <tag2>
        </tag2>
    </tag1>
    <tag/>

    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()
原文地址:https://www.cnblogs.com/liangzhenghong/p/10877811.html