python学习第五周总结
阅读原文时间:2023年07月08日阅读:4
# 编写代码简单的实现人打狗 狗咬人的小游戏(剧情需要)
"""推导步骤1:代码定义出人和狗"""
person1 = {
    'name': 'jason',
    'age': 18,
    'gender': 'male',
    'p_type': '猛男',
    'attack_val': 8000,
    'life_val': 99999999
}
person2 = {
    'name': 'kevin',
    'age': 28,
    'gender': 'female',
    'p_type': '淑女',
    'attack_val': 1,
    'life_val': 100
}
dog1 = {
    'name': '小黑',
    'd_type': '泰迪',
    'attack_val': 100,
    'life_val': 8000
}
dog2 = {
    'name': '小白',
    'd_type': '恶霸',
    'attack_val': 2,
    'life_val': 80000
}
"""
如果想要定义多个人狗,需要多次编写上述字典,采用封装成函数的方式既可以提高效率,又方便随时调用
"""

# 1.定义出人狗的函数
def create_person(name, age, gender, p_type, attack_val, life_cal):
    person_dict = {
        'name': name,
        'age': age,
        'gender': gender,
        'p_type': p_type,
        'attack_val': attack_val,
        'life_val': life_cal
    }
    return person_dict

def create_dog(name, d_type, attack_val, life_val):
    dog_dict = {
        'name': name,
        'd_type': d_type,
        'attack_val': attack_val,
        'life_val': life_val
    }
    return dog_dict

# 2.调用函数,传参,接受函数体代码的返回值
p1 = create_person('max', 25, 'male', '刺客', 10000, 9999999)
print(p1)  # 生成字典然后返回,p1:{'name': 'max', 'age': 25, 'gender': 'male', 'p_type': '刺客', 'attack_val': 10000, 'life_cal': 9999999}
p2 = create_person('kevin', 28, 'female', '淑女', 100, 800)
print(p2)  # {'name': 'kevin', 'age': 28, 'gender': 'female', 'p_type': '淑女', 'attack_val': 100, 'life_cal': 800}

d1 = create_dog('小黑', '恶霸', 800, 900000)  # d1:{'name': '小黑', 'd_type': '恶霸', 'attack_val': 800, 'life_val': 900000}
d2 = create_dog('小白', '泰迪', 100, 800000)  # d2:{'name': '小白', 'd_type': '泰迪', 'attack_val': 100, 'life_val': 800000}

# 3.定义出人打狗、狗咬人的动作
def person_attack(person_dict, dog_dict):
    print(f'人{person_dict.get("name")}准备揍狗{dog_dict.get("name")}')
    dog_dict['life_val'] -= person_dict.get('attack_val')
    print(f'人揍了狗一拳,狗掉血{person_dict.get("attack_val")},剩余血量{dog_dict.get("life_val")}')

def dog_attack(dog_dict, person_dict):
    print(f'狗{dog_dict.get("name")}准备咬人{person_dict.get("name")}')
    person_dict['life_val'] -= dog_dict.get('attack_val')
    print(f'狗咬了人一口,人掉血{dog_dict.get("attack_val")},人剩余血量{person_dict.get("life_val")}')

# 4.正确传参:通过刚才d1、d2、p1、p2通过定义字典函数返回的返回值,把相应字典当做参数传入
person_attack(p1, d1)  # 人max准备揍狗小黑  人揍了狗一拳,狗掉血10000,剩余血量890000
dog_attack(d2, p2)  # 狗小白准备咬人kevin  狗咬了人一口,人掉血100,人剩余血量700

# 5.错误传参:将人的参数传给了狗,狗的参数传给了人。因此得出采用传参的方式极易将犯错误的参数传递给函数
person_attack(d1, p1)  # 人小黑准备揍狗max 人揍了狗一拳,狗掉血800,剩余血量9999199
dog_attack(p2, d2)  # 狗kevin准备咬人小白  狗咬了人一口,人掉血100,人剩余血量799900


"""
如何实现狗只能调用狗的攻击动作,人只能调用人的攻击动作?
"""
def get_person(name, age, gender, p_type, attack_val, life_val):
    def person_attack(person_dict, dog_dict):
        print(f'人{person_dict.get("name")}准备揍狗{dog_dict.get("name")}')
        dog_dict['life_val'] -= person_dict.get('attack_val')
        print(f"人揍了狗一拳 狗掉血:{person_dict.get('attack_val')} 狗剩余血量:{dog_dict.get('life_val')}")
    person_dict = {
        'name': name,
        'age': age,
        'gender': gender,
        'p_type': p_type,
        'attack_val': attack_val,
        'life_val': life_val,
        'person_attack': person_attack
    }
    return person_dict

def get_dog(name, d_type, attack_val, life_val):
    def dog_attack(dog_dict, person_dict):
        print(f"狗:{dog_dict.get('name')}准备咬人:{person_dict.get('name')}")
        person_dict['life_val'] -= dog_dict.get('attack_val')
        print(f"狗咬了人一口 人掉血:{dog_dict.get('attack_val')} 人剩余血量:{person_dict.get('life_val')}")
    dog_dict = {
        'name': name,
        'd_type': d_type,
        'attack_val': attack_val,
        'life_val': life_val,
        'dog_attack': dog_attack
    }
    return dog_dict

person1 = get_person('max', 25, 'male', '猛男', 1000, 80000)  # 调用函数get_person并传参,此时person1 = person_dict
dog1 = get_dog('小黑', '恶霸', 800, 900000)  # 调用函数get_dog并传参,此时dog1 = dog_dict
person1.get('person_attack')(person1, dog1)  # 相当于调用了函数person_attack(person1, dog1),并传入字典person_dict、dog_dict


1.面向过程编程:过程即流程,面向过程就是按照固定的流程解决问题,结局是固定的,也就是功能需求
    eg:注册功能、登录功能、转账功能(需要留举出每一步的流程,并且随着步骤的深入,问题的解决越来越简单)
    结局思路:提出问题,然后指定出解决问题的方案

2.面向对象编程:类似造物主,程序员只需要造出一个个对象,结局有无数个,对象将来会如何发展和程序员没有关系,也无法控制
"""
上述两种编程思想没有优劣之分,需要结合实际需求而定
    如果需求是注册、登陆、人脸识别肯定面相好过程更合适
    如果需求是游戏人物name面向对象更合适
实际编程两种思想是彼此交融的,只不过占比不同
"""


对象:数据与功能的结合体,对象才是核心
类:多个对象相同数据和功能的结合体,类主要功能是为了节省代码
"""
一个人:对象
一群人:人类(所有人相同的特征)
"""
现实中一般是先有对象再有类,程序中如果想要产生对象,必须要先定义出类


面向对象并不是一门新的技术,但是为了更好地适应功能并且便于区分,针对面向对象设计了新的语法格式,python中一定要有类,才能借助于类产生对象
1.类的语法结构:
class 类名:
    '''代码注释'''
    对象公共的数据
    对象公共的功能
    1.1 class是定义类的关键字
    1.2类名的命名与变量名几乎一致,类名的首字母推荐用大写
    1.3数据:变量名与数据值的绑定;功能或方法就是函数

2.类的定义与调用
# 需求:选课系统
#定义类
class Student:
    # 对象公共的数据:
    school_name = '清华大学'
    # 对象公共的功能
    def choice_course(self):
        print('学生选课功能')

类在定义阶段就会执行类体代码,但是类的局部名称空间外无法直接调用,只能在类的局部名称空间使用

    2.1:查看类的局部名称空间中所特有的名字:类名.dict__
print(Student.__dict__)
    2.2:拿到类的局部名称空间中的名字:类名.dict__.get('类中的名字')
        '''类中的名字要加引号'''
print(Student.__dict__.get('name'))
    2.3上述过程太过繁琐,可以直接用类名点名字的方式拿到类中的名字
print(Student.name)  # jason
print(Student.choice_course)  # <function Student.choice_course at 0x000001DD04C7E730>
print(Student.choice_course(111))  # 选课系统  None
    2.4类名加括号可以产生一个对象,并且每次都会产生全新的对象
obj1 = Student()
obj2 = Student()
obj3 = Student()
print(obj1, obj2, obj3)  # <__main__.Student object at 0x000001F81E91B0B8> <__main__.Student object at 0x000001F81E9DC588> <__main__.Student object at 0x000001F81E9DC940>
    2.5类名中本身什么都没有,类Student目前产生了三个对象,本身为空,由于它们产生于一个类,它们可以拿到类Student的数据和功能
print(obj1.name)  # jason
print(obj2.name)  # jason
print(obj3.name)  # jason
    2.6通过类名点的方式修改类内部的名字,类内部的名字是公共的,当类内部的名字修改后,所有对象拿到的名字都是修改后的名字
Student.name = 'max'
print(obj1.name)  # max
print(obj2.name)  # max
print(obj3.name)  # max
"""
数据和功能也可以称为属性,数据可能会被称为属性名
"""


类当中都是对象共有的名字,但是每个对象也可以拥有自己单独的数据。在类中定义功能的时候把函数名替换成__init__,用self点的方式来赋值,底层原理是将传入功能的参数赋值给self点的变量名,self点的变量名其实是字字符串只不过在这里不需要加引号。通过类名括号内传参的方式将参数传递给功能,然后通过类名点的方式就可以拿到对象独有的数据。
class Student:
    school_name = '清华大学'

    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

stu1 = Student('max', 25, 'male')
print(stu1.name)  # max
print(stu1.gender)  # male
stu2 = Student('jason', 18, 'male')
print(stu2.__dict__)  # {'name': 'jason', 'age': 18, 'gender': 'male'}
print(stu2.name)  # jason


定义在类中的功能 默认就是绑定给对象使用的 谁来调谁就是主人公。类调用功能需要手动传参,而对象调用不需要传参,因为对象会将自身当做第一个参数传入功能。如果功能需要多个参数的话,对象调用功能需将第一个参数以外的参数手动传入
class Student:

    def choice_course(self):
        print('选课系统')

Student.choice_course(111)  # 选课系统
stu1 = Student()
stu1.choice_course()  # 选课系统

class Student:

    def choice_course(self, name):
        print('选课系统')

stu1 = Student()
stu1.choice_course('max')  # 选课系统


1.在类中定义的函数(功能有多重属性)
"""
类名加括号会产生一个对象,对象用点函数名的方式不用传参,因为对象对被当做第一个参数自动传入,类名点函数名需要传参
"""
class Group:
    s = 'from Group'
    def func1(self):
        print('from func1')

gro = Group()
gro.func1()  # from func1
Group.func1(111)  # from func1

2.被@classmethond修饰的函数默认绑定给类,类会将自身当做第一个参数传入,所以类调用函数不需要传参。对象调永也不需要传参
class Group:
    s = 'from group'

    @classmethod
    def func1(cls):
        print('from func1')

Group.func1()  # from func1
gro = Group()
gro.func1()  # from func1

3.被@staticmethod修饰的函数就是一个普普通通的函数,不管是类还是对象,调用内部的函数都需要传参
class Group:
    @staticmethod
    def func1(name):
        print('from func1')

Group.func1('max')  # from func1
gro = Group
gro.func1('max')  # from func1


"""
1.面向对象的三大特征:封装、继承、多态
2.三者中继承最为核心
3.封装和多态略为抽象
"""
1.继承的含义:
    在现实生活中表示人与人之间资源的从属关系
    在编程世界里表示类与类之间资源的从属关系
2.继承的目的:
    和现实生活中类似,编程中继承表示不同类之间资源从属关系,比如B类继承A类,那么B类可以共享A类中的数据和功能
3.继承实操:
    3.1在定义类的时候类名后面可以加括号填写其他类名,表示继承该类名
class Student:
    name = 'max'

class Teacher(Student):
    pass

tea1 = Teacher()
print(tea1.name)  # max
    3.2在python中支持多继承,括号内填写多个类名,用逗号隔开
class A1:
    name = 'max'

class A2:
    age = 25

class A3:
    gender = 'male'

class A4(A1, A2, A3):
    pass

a = A4()
print(a.name, a.age, a.gender)  # max 25 male
"""
1.继承其他类的类,我们称之为子类、派生类
2.被继承的类,我们称之为父类
"""


"""
通过对象和类的概念引出继承的本质:
对象:数据与功能的结合体
类:多个对象相同数据和功能的结合体
父类:多个类(子类)相同数据和功能的结合体
类与父类本质都是为了节省代码
"""
继承本质应该分为两部分:
    抽象:将多个类相同的东西抽出去形成一个新的类
     继承:将多个类继承刚刚抽取出来的新的类


1.不继承情况下名字的查找顺序:
    1.1:当对象中有名字,首先从对象的名称空间查找
class A1:
    name = 'jason'

a = A1()
print(a.__dict__)  # {}
a.name = 'max'
print(a.name)  # max
"""
因为查找的是对象的名字,所以每次查找的名字之前一定要先生成一个对象,并且用对象名点名字
"""
    1.2当对象中没有名字时,去类中查找
class Student:
    name = 'max'

stu1 = Student()
print(stu1.name)  # max
    1.3如果类中也没有,那么直接报错
class Student:
    name = 'max'

stu1 = Student()
print(stu1.age)  # 报错
"""
对象名称空间>>>:类(没有即报错)
"""
2.单继承情况下名字查找顺序:
    2.1:对象名称空间有要找的名字时,直接去对象名称空间找
class A1:
    name = 'from A1'

class A2(A1):
    pass

a2 = A2()
a2.name = 'from a2'
print(a2.name)  # from a2
    2.2当对象名称空间中没有时,去产生对象的类名称空间中查找
class A1:
    name = 'from A1'

class A2(A1):
    name = 'from A2'

a2 = A2()
print(a2.name)  # from A2
    2.3当类名称空间中没有时,去父类名称空间中查找
class A1:
    name = 'from A1'

class A2(A1):
    pass

a2 = A2()
print(a2.name)  # from A1
"""
对象名称空间>>>产生对象的类名称空间>>>父类(没有即报错)
"""
3.多继承情况下名字查找顺序:
    3.1对象名称空间中有时首先找对象名称空间
class A:
    name = 'from A'

class B:
    name = 'from B'

class C:
    name = 'from C'

class S(A, B, C):
    name = 'from S'

obj = S()
obj.name = 'obj名称空间中的name'
print(obj.name)  # obj名称空间中的name
    3.2对象名称空间中没有时,从产生它的类名称空间中查找
class A:
    name = 'from A'

class B:
    name = 'from B'

class C:
    name = 'from C'

class S(A, B, C):
    name = 'from S'

obj = S()
print(obj.name)  # from S
    3.3类名称空间没有时直接从父类名称空间中查找(从左往右)
class A:
    name = 'from A'

class B:
    name = 'from B'

class C:
    name = 'from C'

class S(A, B, C):
    pass

obj = S()
print(obj.name)  # from A

4.非菱形继承
class G:
    name = 'from G'

class A:
    name = 'from A'

class B:
    name = 'from B'

class C:
    name = 'from C'

class D(A):
    name = 'from D'

class E(B):
    name = 'from E'

class F(C):
    name = 'from F'

class S1(D, E, F):
    pass

obj = S1()
print(obj.name)
"""
非菱形继承查找顺序首先查找对象名称空间,在查找产生对象的类名称空间,如果没有继续查找父类名称空间,父类名称空间查找顺序为从左往右,从下往上,也可以用print(s1.mro)方法来查看查找顺序,上述题目在父类D名称空间可以找到name,如果找不到再去其父类A中查找。
"""
5.菱形继承
class G:
    name = 'from G'
    pass
class A(G):
    name = 'from A'
    pass
class B(G):
    name = 'from B'
    pass
class C(G):
    name = 'from C'
    pass
class D(A):
    name = 'from D'
    pass
class E(B):
    name = 'from E'
    pass
class F(C):
    name = 'from F'
    pass

class S1(D,E,F):
    pass
obj = S1()
print(obj.name)
"""
菱形查找顺序为先从对象你名称空间中查找,再到类名称空间,再到父类名称空间。父类名称空间查找顺序从左到右,走道菱形交汇点之前停止,直到所有的类都找完,再找交汇点的类。
"""


"""
经典类:不继承object或其子类的类
新式类:继承object或其子类的类
在python2中有经典类和新式类
在python3中只有新式类(所有类都默认继承objecr)
"""
class Student(object):pass
为了更好地兼容python2,以后我们在定义类的时候,如果没有其他明确的父类,也可以直接在括号内加上object


1.子类基于父类做扩展:Student的父类为Person,说明在类Student中可以用到类Person中的名字,但是类Student还想扩展一个名字level,此时可以用到关键字super().__init__(父类所有的名字),然后再扩展新增自己的名字
class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

class Student(Person):
    def __init__(self, name, age, gender, level):
        super().__init__(name, age, gender)
        self.level = level

stu1 = Student('max', 25, 'male', 2)
print(stu1.__dict__)  # {'name': 'max', 'age': 25, 'gender': 'male', 'level': 2}
print(stu1.name)  # max

class Teacher(Person):
    def __init__(self, name, age, gender, grade):
        super().__init__(name, age, gender)
        self.grade = grade

tea1 = Teacher('jason', 18, 'male', 9)
print(tea1.__dict__)  # {'name': 'jason', 'age': 18, 'gender': 'male', 'grade': 9}