ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

本周内容总结

2022-07-31 23:36:03  阅读:103  来源: 互联网

标签:总结 __ obj name self 本周 内容 print def


周总结

  • 阶段一
  • 面向对象前戏
  • 编程思想(面向过程、面向对象)
  • 类与对象的概念
  • 类与对象的创建
  • 对象的实例化方法(独有数据)
  • 对象的绑定方法(独有功能)
  • 阶段二
  • 对象独有的功能
  • 动静态方法
  • 面向对象三大特诊之继承
  • 继承的本质
  • 数据和方法的查找顺序
  • 经典类与新式类
  • 派生方法
  • 阶段三
  • 派生方法的实战演练
  • 面向对象三大特性之封装
  • property伪装属性
  • 面向对象三大特性之多态
  • 面向对象之反射(重要)
  • 反射实战演练(重要)
  • 阶段四
  • 反射实战案例
  • 面向对象的魔法方法(双下方法)
  • 魔法方法实战演练
  • 元类简介
  • 创建类的两种方式
  • 元类的实际应用
  • 元类之双下new方法

阶段一

面向对象前戏

  • 模拟人狗大战
  • 推导1:用字典去模拟独立的人和独立的狗
# person1 = {  # 使用字典模拟人
#     'name': 'jason',
#     'p_type': '猛男',
#     'attack_val': 800,
#     'life_val': 2000
# }


# dog1 = {  # 使用字典模拟狗
#     'name': '小黑狗',
#     'd_type': '泰迪',
#     'attack_val': 50,
#     'life_val': 800
# }
  • 推导步骤2: 由于定义人和狗的字典基本不变 但是在很多地方又需要反复使用 所以封装成函数
def get_person(name, gender, age, p_type, attack_val, life_val):
    """
    专用用于产生用户字典(创造人)
    :param name: 姓名
    :param gender: 性别
    :param age: 年龄
    :param d_type: 类型
    :param attack_val:攻击力
    :param life_val: 生命值
    :return: 人的字典(人)
    """
    person_obj = {
        'name': name,
        'gender': gender,
        'age': age,
        'p_type': p_type,
        'attack_val': attack_val,
        'life_val': life_val
    }
    return person_obj

# p1 = get_person('jason', 'male', 18, '猛男', 8000, 80000)  #  # 可以随意传入不同人的数据 产生不同人对象的数据字典
def get_dog(name, d_type, attack_val, life_val):
    """
    专门用于产生狗字典(狗)
    :param name: 狗的名字
    :param d_type: 狗的类型
    :param attack_val: 狗的攻击力
    :param life_val: 狗的生命值
    :return: 狗的字典(狗)
    """
    dog_obj = {
        'name': name,
        'd_type': d_type,
        'attack_val': attack_val,
        'life_val': life_val
    }
    return dog_obj
# d1 = get_dog('小黄狗','中华田园犬',800, 5000)  # 可以随意传入不同狗的数据 产生不同狗对象数据字典
  • 推导步骤3: 让人和狗具备攻击的能力 本质其实就是定义两个函数供人和狗调用
def person_attack(person_obj, dog_obj):
    """
    专用提供给人调用 攻击狗
    :param person_obj: 传人数据(字典)
    :param dog_obj: 传狗数据(字典)
    """
    print('即将被攻击的狗:%s 当前血量:%s' % (dog_obj.get('name'), dog_obj.get('life_val')))  # 先展示当前狗的状态
    dog_obj['life_val'] -= person_obj.get('attack_val')  # 人锤狗 直接用狗的生命值减去人的攻击力
    print('人:%s 锤了 狗:%s 狗掉血:%s 剩余血量:%s' % (
        person_obj.get('name'), dog_obj.get('name'), person_obj.get('attack_val'), dog_obj.get('life_val')))


def dog_attack(dog_obj, person_obj):
    """
    专用提供给狗调用 攻击人
    :param dog_obj: 传狗数据(字典)
    :param person_obj: 传人数据(字典)
    """
    print('即将被攻击的人:%s 当前血量:%s' % (person_obj.get('name'), person_obj.get('life_val')))  # 先展示当前人的状态
    person_obj['life_val'] -= dog_obj.get('attack_val')  # 狗咬人 直接用人的生命值减去狗的攻击力
    print('狗:%s 咬了 人:%s 人掉血:%s 剩余血量:%s' % (
        dog_obj.get('name'), person_obj.get('name'), dog_obj.get('attack_val'), person_obj.get('life_val')))
    
# 正常操作方式是:
person_attack(p1, d1)  # 人攻击狗
dog_attack(d1,p1)  # 狗咬人
# 也可以这样去操作:
person_attack(d1, p1)  # 这样人和狗的数据就乱套了

			

  • 推导步骤4: 人跟人攻击狗的函数绑定 狗跟狗攻击人的函数绑定
    我们定义的函数默认情况下都是可以被任意调用的 (人和狗攻击乱套了)

    但是现在我们想实现定义的函数只有特定的东西才可以调用

def get_person(name, gender, age, p_type, attack_val, life_val):
    """
    专用用于产生用户字典(创造人)
    :param name: 姓名
    :param gender: 性别
    :param age: 年龄
    :param d_type: 类型
    :param attack_val:攻击力
    :param life_val: 生命值
    :return: 人的字典(人)
    """
    def person_attack(person_obj, dog_obj):
        """
        专用提供给人调用 攻击狗
        :param person_obj: 传人数据(字典)
        :param dog_obj: 传狗数据(字典)
        """
        print('即将被攻击的狗:%s 当前血量:%s' % (dog_obj.get('name'), dog_obj.get('life_val')))  # 先展示当前狗的状态
        dog_obj['life_val'] -= person_obj.get('attack_val')  # 人锤狗 直接用狗的生命值减去人的攻击力
        print('人:%s 锤了 狗:%s 狗掉血:%s 剩余血量:%s' % (
            person_obj.get('name'), dog_obj.get('name'), person_obj.get('attack_val'), dog_obj.get('life_val')))
    person_obj = {
        'name': name,
        'gender': gender,
        'age': age,
        'p_type': p_type,
        'attack_val': attack_val,
        'life_val': life_val,
        'person_attack':person_attack
    }
    return person_obj


def get_dog(name, d_type, attack_val, life_val):
    """
    专门用于产生狗字典(狗)
    :param name: 狗的名字
    :param d_type: 狗的类型
    :param attack_val: 狗的攻击力
    :param life_val: 狗的生命值
    :return: 狗的字典(狗)
    """
    def dog_attack(dog_obj, person_obj):
        """
        专用提供给狗调用 攻击人
        :param dog_obj: 传狗数据(字典)
        :param person_obj: 传人数据(字典)
        """
        print('即将被攻击的人:%s 当前血量:%s' % (person_obj.get('name'), person_obj.get('life_val')))  # 先展示当前人的状态
        person_obj['life_val'] -= dog_obj.get('attack_val')  # 狗咬人 直接用人的生命值减去狗的攻击力
        print('狗:%s 咬了 人:%s 人掉血:%s 剩余血量:%s' % (
            dog_obj.get('name'), person_obj.get('name'), dog_obj.get('attack_val'), person_obj.get('life_val')))
    dog_obj = {
        'name': name,
        'd_type': d_type,
        'attack_val': attack_val,
        'life_val': life_val,
        'dog_attack':dog_attack
    }
    return dog_obj

d1 = get_dog('小黄狗', '恶霸犬', 800, 8000)
p1 = get_person('jason', 'male', 18, '猛男', 8000, 90000)

# 这样绑定在一起就无法去随意的变换人与狗的参数位置了
p1.get('person_attack')(p1, d1)

d1.get('dog_attack')(d1, p1)
  • 总结:

    • 将人的数据跟人的功能绑定到一起
      只有人可以调用人的功能
      将狗的数据跟狗的功能绑定到一起
      只有狗可以调用狗的功能
      我们将数据与功能绑定到一起的操作起名为:'面向对象编程'

      本质:将特定的数据与特定的功能绑定到一起 将来只能彼此相互使用

编程思想

  • 面对过程编程
    • 没学面向对象之前 所编写的代码都是面向过程(按照步骤去编写依次步骤 依次执行 最终可以得到想要的结果)
  • 面对对象编程
    • 核心就是"对象"二字
    • 对象其实就是一个容器 里面将数据和功能绑定到了一起
    • eg: 游戏人物 ...
      只负责创造出该人物以及该人物具备的功能 至于后续战绩如何无人知晓
  • 面向过程编程相当于让你给出一个问题的具体解决方案
  • 面向对象编程相当于让你创造出一些事物之后不用你管
  • 上述两种编程思想没有优劣之分 仅仅是使用场景不同
    甚至很多时候是两者混合使用

对象与类的概念

  • 对象:数据和功能的结合体

  • 类:多个对象相同的数据和功能的结合体

  • 对象则用于记录多个对象不同的数据和功能

  • 类主要用于记录多个对象相同的数据和功能

  • 作用:

    • 在面向对象编程中 类仅仅是用于节省代码 对象才是核心

对象与类的创建

1.建类的完整语法
class Student:
2.Student是类的名字
	类名的命名跟变量名一致 并且推荐首字母大写(为了更好的区分)
3.类体代码
	公共的数据\公共的方法
ps:类体代码在类定义阶段就会执行!!!

    
# class Student:
#     school = '清华大学'
#
#     def choice_course(self):
#         print('正在选课')

# 查看名称空间的方法
# print(Student.__dict__)  # 使用该方法查看名称空间 可以看成是一个字典
# print(Student.__dict__['school'])  # 使用字典的取值方式获取名字
# print(Student.__dict__.get('choice_course'))  # 使用字典的取值方式获取名字
'''在面向对象编程中 想要获取名称空间中的名字 可以采用句点符'''

print(Student.school)  # 清华大学
print(Student.choice_course)  # <function Student.choice_course at 0x0237B028>

'''类实例化产生对象: 类名加括号'''
stu = Student()
print(stu)  # <__main__.Student object at 0x0151A2F8>
print(stu.school)  # 清华大学
# print(stu1)  # <__main__.Student object at 0x000001D923B04A60>
# print(stu2)  # <__main__.Student object at 0x0000025E8A48F130>
# print(stu1.__dict__, stu2.__dict__)  # {} {}
Student.school = '复旦大学'  # 修改school键对应的值
stu = Student()
stu1 = Student()
print(stu.school)  # 复旦大学
print(stu1.school)  # 复旦大学
"""
我们习惯将类或者对象句点符后面的东西称为属性名或者方法名
"""

对象独有的数据

# 学生类
# class Student:
#     # 学生对象公共的数据
#     school = '清华大学'
#
#     # 学生对象公共的方法
#     def choice_course(self):
#         print('正在选课')

'''推导思路1:  直接利用__dict__方法朝字典添加键值对'''
# obj1 = Student()
# obj1.__dict__['name'] = 'jason'  # 等价于 obj1.name = 'jason'
# obj1.__dict__['age'] = 18  # 等价于 obj1.age = 18
# obj1.__dict__['gender'] = 'male'  # ...
# print(obj1.name)
# print(obj1.age)
# print(obj1.gender)
# print(obj1.school)
# obj2 = Student()
# obj2.__dict__['name'] = 'kevin'
# obj2.__dict__['age'] = 28
# obj2.__dict__['gender'] = 'female'
# print(obj2.name)
# print(obj2.age)
# print(obj2.gender)
# print(obj2.school)
'''推导思路2: 将添加独有数据的代码封装成函数'''
# def init(obj,name,age,gender):
#     obj.__dict__['name'] = name
#     obj.__dict__['age'] = age
#     obj.__dict__['gender'] = gender
# stu1 = Student()
# stu2 = Student()
# init(stu1,'jason',18,'male')
# init(stu2, 'kevin',28,'female')
# print(stu1.__dict__)
# print(stu2.__dict__)
'''推导思路3: init函数是专用给学生对象创建独有的数据 其他对象不能调用>>>:面向对象思想   将数据和功能整合到一起
将函数封装到学生类中 这样只有学生类产生的对象才有资格访问
'''


class Student:
    """
    1.先产生一个空对象
    2.自动调用类里面的__init__方法 将产生的空对象当成第一个参数传入
    3.将产生的对象返回出去
    """
    def __init__(self, name, age, gender):
        self.name = name  # ① obj.__dict__['name'] = name 
        self.age = age  # ① obj.__dict__['age'] = age # 
        self.gender = gender  # ① obj.__dict__['gender'] = gender
        # 左右两边的名字虽然一样 但是意思不一样 左边的其实是字典的键 右边的其实是实参

    # 学生对象公共的数据
    school = '清华大学'

    # 学生对象公共的方法
    def choice_course(self):
        print('正在选课')

# 用方法① 可以调用 但是比较麻烦不简便 可以用下面的方法
# stu1 = Student()
# print(stu1.__dict__)
# Student.init(stu1, 'jason', 18, 'male')
# print(stu1.__dict__)
# print(stu1.name)

# 直接类实例化产生对象传参
stu1 = Student('jason', 18, 'male')
print(stu1.__dict__)  # {'name': 'jason', 'age': 18, 'gender': 'male'}
stu2 = Student('kevin', 28, 'female')
print(stu2.__dict__)  # {'name': 'kevin', 'age': 28, 'gender': 'female'}

阶段二

对象独有的功能

  • 针对对象独有的方法 我们无法真正实现
    • 1.如果在全局则不是独有的
    • 2.如果在类中则是公共的
  • python解释器针对上述问题添加了一个非常牛的特性(init
  • 定义在类中的函数默认是绑定给对象的(相当于是对象独有的方法
class Person:
    h_type = '人类'  # 公共对象 类和对象都可以调用

    def __init__(self,name):  # 通过传实参的方式 让对象拥有独有的数据
        
        self.name = name  # 左边的name是k 后面的name 传入的数据值
    def ert(self):  # 定义在类中的函数 称之为方法
        print('%s在玩游戏'% self.name)  # 是多个对象公共的方法 也算是对象独有的方法 因为每个对象传入的实参数据不同  来调用的时候 会自动把对象当成第一个参数传入 
    def inner(self,a,b):  # 对象调用此方法时 只要传入两个实参就可以了 ② 类调用时候 需要传入三个实参 并且最好一个参数传入的是对象
        print('来自inner')
 

p1 = Person('make')
print(p2.name)  # jaosn
p1.ert()  # make在玩游戏
Person.ert(p1)  # make在玩游戏

#  利用传入不同的数据 绑定功能 让公共的方法成为对象独有的调用的方法

p2 = Person('jaosn')
print(p2.name)  # jaosn
p2.ert()  # jaosn在玩游戏
Person.ert(p2)  # jaosn在玩游戏

# 调用inner方法
p3 = Person('tony')  
p1.others(1, 2)  # 来自inner  对象调用此方法时 只要传入两个实参就可以了
Person.others(p1,1,2)  # 来自inner  类调用时候 需要传入三个实参 并且最好一个参数传入的是对象

动静态方法

  • 专门针对在类体代码中去编写的函数
  • 1.绑定给对象的方法
    • 直接在类体中编写代码即可
    • 对象调用会自动把对象自己当做第一个参数传入
    • 类调用时 则有几个参数就传入就个参数
class Person:
    h_type = '人类'

    def eat(self):  # self用于接收对象
        print('在不在',self)
        
p1 = Person()  
p1.eat()  # 对象调用会自动将对象当做第一个参数传入
Person.eat(p1)  # 类调用则有几个形参就传几个实参


  • 2.绑定给类的方法(关键字:@classmethod)
class Person:
    h_type = '人类'

    @classmethod  # 绑定给类的方法
    def eat(cls):  # cls用于接收类 也是class的缩写
        print('吃不吃',cls)

        
Person.eat()  # 吃不吃 <class '__main__.Person'>
# 类可以直接调用 不用传参 类调用会自动将类当做第一个参数传入
p1 = Person()
p1.eat()  # 吃不吃 <class '__main__.Person'>
# 对象也可以直接调用方法  对象调用会自动将产生该对象的类当做第一个参数传入
        

  • 3.静态方法(普普通通的函数:@staticmethod)
class Person:
    h_type = '人类'

    @staticmethod # 静态方法
    def run(a,b):  # 无论谁调用此方法都需要按照普普通通的函数传参方式
        print('跑不跑')
Person.run(1,2)  # 跑不跑 (类调用 按照方法里的形参的个数 传入相同的个数的实参)
p1 = Person()
p1.run(1,2)  # 跑不跑 (对象调用 按照方法里的形参的个数 传入相同的个数的实参)

面向对象三大特征之继承

  • 1.继承的含义
    • 在编程世界里继承其实就是用来描述类与类之间数据的关系
    • eg:类A继承类B(拥有了类B里面所有的数据和功能)
  • 2.继承的目的
    • 编程世界里继承就是为了节省代码编写
    • eg:可以继承一个类 也可以继承多个类
  • 3.继承的操作
class 类名(父类):
        pass
    
	1.定义类的时候在类名后加括号
	2.括号内填写你需要继承的类名
 	3.括号内可以填写多个父类 逗号隔开即可
	"""
	我们将被继承的类称之为: 父类或基类或超类
	我们将继承类的类称之为: 子类或派生类
	ps:平时最常用的就是父类和子类
	"""
	class MyClass(F1,F2,F3):
    pass

ps:从左到右(F1~F3)按照顺序 查找每个父类中的属性即可

继承的本质

  • 抽象:将多个类共同的数据或功能抽取出来 封装成一个基类

  • 继承:从上往下去获取各个基类里面的资源

    • 对象:数据和功能的结合体
    • 类:多个对象相同的数据和功能的结合体
    • 父类:多个类相同的数据和功能的结合体
    • 类和父类主要的功能就是为了节省代码

名字的查找顺序

  • 1.不继承的情况下名字的查找顺序
    • 先从对象自身查找 如果找不到 再去产生对象的类中去查找
    • (顺序:对象>>类)
    • 关键字:mor()
    • 使用类点mro()方法查看该类产生的对象名字的查找顺序
       
    class Student:
            school = '清华大学'
            def choice_course(self):
                print('正在选课')
        stu1 = Student()
        print(stu1.school)  # 清华大学 # 对象查找school 自身名称空间没有 所以查找的是类的  清华大学
        stu1.school = '北京大学'  # 在对象自身的名称空间中产生了新的 school 赋的值
        ps:对象点名字并写了赋值符号和数据值 那么操作的肯定是自己的名称空间
        print(stu1.school)  # 北京大学  # 对象查找school 自身名称空间中有 所以值为 北京大学2
        print(Student.school)  # 清华大学   # 在类名称空间中 school 的值还是 清华大学
       

  • 2.单继承的情况下名字的查找顺序
    • 先从对象自身查找 然后是产生该对象的类 然后是一个个父类
    • (顺序:对象 >>> 类 >>> 父类...)
    • 只要涉及到对象查找名字 几乎要回到最开始的位置依次查找
class A:
        # name = 'from A'
        pass
    class B(A):
        # name = 'from B'
        pass
    class C(B):
        # name = 'from C'
        pass
    class MyClass(C):
        # name = 'from MyClass'
        pass
    obj = MyClass()
    # obj.name = '我很困!!!'
    print(obj.name)  # 我很困!!!
===================顺序====================
	对象 >>> 类 >>> 父类...
=====================演练=====================
    class A1:
    def func1(self):
        print('from A1 func1')
    def func2(self):
        print('from A1 func2')
        self.func1()  # obj.func1()
    class MyClass(A1):
        def func1(self):
            print('from MyClass func1')
    obj = MyClass()
    obj.func2()  #  # from A1 func2
# from MyClass func1
====
ps:涉及对象查找名字(功能) 每找一次就要回到自己最开始的地方 依次查找  以免遗漏====
  • 多继承的情况下名字的查找顺序

    • 非菱形继承(最后不会归总到一个我们自定义类上)

      • 深度优先(每个分支都走到底 再切换)
    • 菱形继承(最后归总到一个我们自定义类上)

      • 广度优先(前面几个分支都不会走最后一个类 最后一个分支才会走)
    • 主要涉及到对象查找名字顺序几乎都为:

    • 对象自身>>>类>>>父类

经典类与新式类

  • 经典类
    • 不继承object或其子类的类(什么都不继承)
    • 在python2中有经典类和新式类
    • 由于经典类没有核心的功能 所以到了python3直接砍掉了
  • 新式类
    • 继承了ojbect或其子类的类
    • 在python3中所有的类默认都会继承object
    • 也就意味着python3里面只有新式类
  • 提醒:
    • 以后我们在定义类的时候 如果没有想要继承的父类 一般推荐以下写法:
      class MyClass(object):
      pass
      目的是为了兼容python2

派生方法

  • 本质:
    • 子类中继承了父类的所有功能 并且还扩展了其他功能
    • 关键字:super()
class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender


#
class Teacher(Person):
    def __init__(self, name, age, gender, level, hobby, Height,ab):
        # Person.__init__(self,name,age,gender)  # 先调用父类的方法
        super().__init__(name, age, gender)  # super专门用于子类调用父类的方法
        self.level = level  # 可以补充想添加的数据
        self.hobby = hobby
        self.Height = Height
    



t1 = Teacher('小伍', 18, 'man', '王者','台球',210,'haha')
print(t1.level)  # 王者
print(t1.hobby)  # 台球
print(t1.Height)  # 210

阶段三

派生方法的实战演练(重要)

  • 基于父类的基础上改写添加新的功能
import datetime
import json


d = {
    't1': datetime.datetime.today(),
    't2': datetime.date.today()
}
res = json.dumps(d)
print(res)
# 结果报错:
"""
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type datetime is not JSON serializable
"""
# 原因为json序列化python中的数据对象是有规定的 不是所有的类型都能序列化 

  • 即将要被序列化的数据 里里外外都必须是上述类型才可以
python json
dict object
list,tuple array
str string
int, float number
True true
False false
None null
  • 处理方式一
    • 手动将不符合数据类型要求的数据转成符合要求的(str类型转换)
import datetime
import json

d = {
    't1': str(datetime.datetime.today()),
    't2': str(datetime.date.today())
}
res = json.dumps(d)
print(res)  # {"t1": "2022-07-28 16:41:34.215700", "t2": "2022-07-28"}

  • 处理方式二
    • 派生方法
import datetime
import json


d = {
    't1': datetime.datetime.today(),
    't2': datetime.date.today()
}
res = json.dumps(d)
print(res)
# 上述打印会打印报错 
# 通过查看json.dumps 和 json.JSONEncoder 的底层源码 我们发现
# dumps(obj,cls=None):  # 里面的第一个形参 obj 是来接收传入的数据  如果不给cls传值
#    if cls == None:  # 默认cls == None
#        cls = JSONEncoder
#    return cls(...)  # 返回cls就是返回 JSONEncoder  因为  JSONEncoder 是个类 我们直接研究它就可以了
# class JSONEncoder:
#    pass        
# 查看JSONEncoder源码发现序列化报错是有default方法触发的
# raise TypeError(f'Object of type {o.__class__.__name__} '
#                         f'is not JSON serializable')
# 我们如果想要避免报错 那么肯定需要对default方法做修改(派生)

class MyJsonEncode(json.JSONEncoder):
    def default(self, o):
        '''o就是json即将要序列化的数据'''
        if isinstance(o, datetime.datetime):  # 做判断o如果是datetime.datetime
            return o.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(o, datetime.date):  # 做判断o如果是datetime.date
            return o.strftime('%Y-%m-%d')
        # 如果是可以序列化的类型 那么不做任何处理 直接让它序列化即可
        return super().default(o)


res = json.dumps(d, cls=MyJsonEncode)
print(res)  # "t1": "2022-07-28 17:29:47", "t2": "2022-07-28"}  实现json格式的字符串

面向对象三大特征之封装

  • 本质:

    • 封装其实就是将数据或者功能隐藏起来(包起来 装起来)

    • 隐藏的目的不是让用户无法使用 而是给这些隐藏的数据开设特定的接口 让用户使用接口才可以去使用 我们在接口中添加一些额外的操作

  • 1.在类定义阶段使用双下划线开头的名字 都是隐藏的属性

    • 后续类和对象都无法直接获取
  • 2.在python中不会真正的限制任何代码

    • 调用但是需要用特殊的方式去处理
      • 隐藏时:__变量名
      • 调用时:对象._类名__变量名
  • 既然在类体内用了 隐藏方式 就不要使用变形后的名字去访问 这样就失去了隐藏的意义 (会给你接口去访问隐藏的数据的)

class Student(object):
    __school = '清华大学'
    def __init__(self, name, age):
        self.__name = name
        self.__age = age
    # 专门开设一个访问学生数据的通道(接口)
    def check_info(self):
        print("""
        学生姓名:%s
        学生年龄:%s
        """ % (self.__name, self.__age))
    # 专门开设一个修改学生数据的通道(接口)
    def set_info(self,name,age):
        if len(name) == 0:
            print('用户名不能为空')
            return
        if not isinstance(age,int):
            print('年龄必须是数字')
            return
        self.__name = name
        self.__age = age

obj = Student('jason',18)
obj.check_info()  # 学生姓名:jason  学生年龄:18
obj.set_info('make',19)
obj.check_info()  # 学生姓名:mak   学生年龄:19

property伪装属性

  • 伪装属性可以理解为将方法伪装成数据

    • 正常情况下 对象直接就可以点出数据obj.school
    • 正常情况下 对象点方法时需要加上括号才可以使用 obj.func()
    • 趣味了解:体质指数(BMI)=体重(kg)÷身高^2(m)
    • 加上伪装属性:@property
class Person:
    def __init__(self, name, weight, height):
        self.name = name
        self.weight = weight
        self.height = height

    
    def BMI(self):
        return self.weight / (self.height ** 2)

p1 = Person('小五',75 , 1.78)
res = p1.BMI()  # 调用方法需要这样操作
print(res)  # 23.671253629592222

"""
加上伪装属性:@property
"""
class Person:
    def __init__(self, name, weight, height):
        self.name = name
        self.weight = weight
        self.height = height

    @property
    def BMI(self):
        return self.weight / (self.height ** 2)

p1 = Person('小五',75 , 1.78)
print(p1.BMI)  
# 23.671253629592222  不需要加括号 和调用数据一样  直接就打印出来(伪装)

面向对象三大特性之多态

多态:一种事物的多种形态
 	水:液态 气态 固态
 	动物:人 狗 猫 猪
        
class Animal(object):
    def spark(self):
        pass


class Cat(Animal):
    def spark(self):
        print('喵喵喵')


class Dog(Animal):
    def spark(self):
        print('汪汪汪')


class Pig(Animal):
    def spark(self):
        print('哼哼哼')


# c1 = Cat()
# d1 = Dog()
# p1 = Pig()
# c1.miao()
# d1.wang()
# p1.heng()
"""
一种事物有多种形态 但是相同的功能应该有相同的名字
这样的话 以后我无论拿到哪个具体的动物 都不需要管到底是谁 直接调用相同的功能即可
    无论你是鸡 鸭 猫 狗 猪 只要你想叫 你就调固定的叫的功能
"""
# c1.spark()
# d1.spark()
# p1.spark()

"""
其实上述多态的概念 我们很早之前就已经解除过
"""
# l1 = [11, 22, 33, 44]
# d1 = {'name': 'jason', 'pwd': 123, 'hobby': 'raed'}
# t1 = (11, 22, 33, 44)
# print(len(l1))
# print(len(d1))
# print(len(t1))


"""
python也提供了一种强制性的操作(了解即可)  应该是自觉遵守
"""
# import abc
# # 指定metaclass属性将类设置为抽象类,抽象类本身只是用来约束子类的,不能被实例化
# class Animal(metaclass=abc.ABCMeta):
#     @abc.abstractmethod # 该装饰器限制子类必须定义有一个名为talk的方法
#     def talk(self): # 抽象方法中无需实现具体的功能
#         pass
# class Person(Animal): # 但凡继承Animal的子类都必须遵循Animal规定的标准
#     def talk(self):
#         pass
#     def run(self):
#         pass
# obj = Person()

"""
鸭子类型
    只要你长得像鸭子 走路像鸭子 说话像鸭子 那么你就是鸭子
"""
# class Teacher:
#     def run(self):pass
#     def eat(self):pass
# class Student:
#     def run(self):pass
#     def eat(self):pass


"""
操作系统
    linux系统:一切皆文件
        只要你能读数据 能写数据 那么你就是文件
            内存
            硬盘
        class Txt: #Txt类有两个与文件类型同名的方法,即read和write
            def read(self):
                pass
            def write(self):
                pass
        
        class Disk: #Disk类也有两个与文件类型同名的方法:read和write
            def read(self):
                pass
            def write(self):
                pass
        
        class Memory: #Memory类也有两个与文件类型同名的方法:read和write
            def read(self):
                pass
            def write(self):
                pass
    python:一切皆对象
        只要你有数据 有功能 那么你就是对象
            文件名         文件对象
            模块名         模块对象       
"""

面向对象之反射

  • 反射:通过字符串来操作对象的数据或方法
  • 反射主要就4个方法
    • hasattr():判断对象中是否含有某个字符串对应的属性
    • getattr():获取对象字符串所对应的属性
    • setattr(): 根据字符串给对象设置属性
    • delattr():根据字符串给对象删除属性
class Student:
    school = '清华大学'

    def choice_course(self):
        print('选课')
stu = Student()
# 需求:判断用户提供的名字在不在对象可以使用的范围内
方式1:利用异常处理(过于繁琐)
try:
    if stu.school:
        print(f"True{stu.school}")
except Exception:
    print("没有属性")
"""
变量名school 与字符串school 区别大不大
    stu.school
    stu.'school'
两者虽然只差了引号 但是本质是完全不一样的
"""
# 方式2:获取用户输入的名字 然后判断该名字对象有没有
# while True:
#     target_name = input('请输入您想要核查的名字>>>:').strip()
#     '''上面的异常更加不好实现 需要用反射'''
#     # print(hasattr(stu, target_name))
#     # print(getattr(stu, target_name))
#     if hasattr(stu, target_name):
#         # print(getattr(stu, target_name))
#         res = getattr(stu, target_name)
#         if callable(res):
#             print('拿到的名字是一个函数', res())
#         else:
#             print('拿到的名字是一个数据', res)
#     else:
#         print('不好意思 您想要查找的名字 对象没有')
print(stu.__dict__)
stu.name = 'jason'
stu.age = 18
print(stu.__dict__)
setattr(stu,'gender','male')
setattr(stu,'hobby','read')
print(stu.__dict__)
del stu.name
print(stu.__dict__)
delattr(stu, 'age')
print(stu.__dict__)

"""
以后只要在需求中看到了关键字
	....对象....字符串 
那么肯定需要使用反射
"""

反射实战案例

class FtpServer:
    def serve_forever(self):
        while True:
            inp = input('input your cmd>>: ').strip()
            cmd, file = inp.split()
            if hasattr(self, cmd):  # 根据用户输入的cmd,判断对象self有无对应的方法属性
                func = getattr(self, cmd)  # 根据字符串cmd,获取对象self对应的方法属性
                func(file)
    def get(self, file):
        print('Downloading %s...' % file)

    def put(self, file):
        print('Uploading %s...' % file)
obj = FtpServer()
obj.serve_forever()

阶段四

反射实战案例

1.加载配置文件纯大写的配置
需求:配置文件加载:获取配置文件中所有大写的配置 小写的直接忽略  组织成字典settings里有以下内容
NAME = 'make'
AGE = 18
name = 'jason'
age = 28
================================================================
import settings
new_dict = {}
# print(dir(settings))  # dir获取括号中对象可以调用的名字
# ['NAME', 'AGE', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'name', 'age']
for i in dir(settings):
    if i.isupper():  # 如果名字是纯大写 那么获取该大写名字对应的值   'NAME'   'AGE'
        v = getattr(settings, i)
        new_dict[i] = v
        print(new_dict)  # {'AGE': 18, 'NAME': 'make'}
2.模拟操作系统cmd终端执行用户命令
	class WinCmd(object):
        def dir(self):
            print('dir获取当前目录下所有的文件名称')

        def ls(self):
            print('ls获取当前路径下所有的文件名称')

        def ipconfig(self):
            print('ipconfig获取当前计算机的网卡信息')
    obj = WinCmd()
    while True:
        cmd = input('请输入您的命令>>>:')
        if hasattr(obj, cmd):
            cmd_name = getattr(obj, cmd)
            cmd_name()
        else:
            print('%s 不是内部或外部命令,也不是可运行的程序或批处理文件' % cmd)

面向对象魔法方法

魔法方法其实就是类中定义的双下方法
	之所以会叫魔法方法原因是这些方法都是到达某个条件自动触发 无需调用
 		eg: __init__方法在给对象设置独有数据的时候自动触发(实例化)
 
下列讲解的魔法方法都必须明确的知道的触发的条件
class MyClass(object):
    def __init__(self, name):
        """实例化对象的时候自动触发"""
        # print('__init__方法')
        # pass
        self.name = name
    def __str__(self):
        """
        对象被执行打印操作的时候会自动触发
            该方法必须返回一个字符串
            返回什么字符串打印对象之后就展示什么字符串
        """
        # print('__str__方法')
        # print('这是类:%s 产生的一个对象')
        # return '对象:%s'%self
        return '对象:%s'%self.name
    def __call__(self, *args, **kwargs):
        """对象加括号调用 自动触发该方法"""
        print('__call__方法')
        print(args)
        print(kwargs)
    def __getattr__(self, item):
        """当对象获取一个不存在的属性名 自动触发
            该方法返回什么 对象获取不存在的属性名就会得到什么
            形参item就是对象想要获取的不存在的属性名
        """
        print('__getattr__', item)
        return '您想要获取的属性名:%s不存在'%item
    def __setattr__(self, key, value):
        """对象操作属性值的时候自动触发>>>:  对象.属性名=属性值"""
        # print("__setattr__")   
        # print(key)
        # print(value)
        super().__setattr__(key, value)
    def __del__(self):
        """对象在被删除(主动 被动)的时候自动触发"""
        # print('__del__')
        pass
    def __getattribute__(self, item):
        """对象获取属性的时候自动触发 无论这个属性存不存在
            当类中既有__getattr__又有__getattribute__的时候 只会走后者
        """
        # print('__getattribute__')
        # return super(MyClass, self).__getattribute__(item)  复杂写法
        return super().__getattribute__(item)  # 简便写法
    def __enter__(self):
        """对象被with语法执行的时候自动触发 该方法返回什么 as关键字后面的变量名就能得到什么"""
        print('__enter__')
    def __exit__(self, exc_type, exc_val, exc_tb):
        """对象被with语法执行并运行完with子代码之后 自动触发"""
        print('__exit__')

魔法方法笔试题

"""补全以下代码 执行之后不报错"""
class Context:
    def __enter__(self):
        pass
    def __exit__(self, exc_type, exc_val, exc_tb):
        pass

with Context() as f:
    f.do_something() 
=================================
# 解决方式:

class Context:
    def __enter__(self):
        return self  # 返回什么f就拿到什么
    def __exit__(self, exc_type, exc_val, exc_tb):
        pass
    def do_something(self):
        pass  # 这一步定义一个方法 给下述做准备
with Context() as f:
    f.do_something()  # f是对象 (Context)类产生的对象  对象.do_something  这个方法  就不会报错 

元类简介

  • 通过下述推导 得出结论 自定义的类都是由type类产生的
  • 我们将产生类的类称之为 '元类'
推导:

s1 = '今天很热'
l1  = [1,232,3,43,345,46,456,5]
d1 = {'name':'make','age':20}
print(type(s1))  # <class 'str'>
print(type(l1))  # <class 'list'>
print(type(d1))  # <class 'dict'>

# 发现:
基础阶段我们使用type来查找数据的数据类型
学了面向对象之后 发现查看的不是数据类型 而是数据所属的类
我们定义的数据类型 其实本质还是通过各个类产生了对象

class str:
    s1 = '今天很热'
print(str.s1)
# 我们也可以理解为type用于查看产生当前对象的类是谁
class MyClass:
    pass
obj = MyClass()
print(type(obj))  # 查看产生对象obj的类:<class '__main__.MyClass'>
print(type(MyClass))  # 查看产生对象MyClass的类:<class 'type'>

产生类的两方式

1.class关键字
	class MyClass:
        pass

2.利用元类type
	type(类名,类的父类,类的名称空间)
 
  • 学习元类其实就是掌握了类的产生过程 我们就可以在类的产生过程中高度定制化类的行为
  • eg:
    类名必须首字母大写
    上述需求就需要使用元类来控制类的产生过程 在过程中校验

元类基本使用

  • 只有继承了type的才能称之为是元类
  • 如想切换产生类的元类 需要使用关键字metaclass 声明
  • 推导:
    • 类中的__init__用于实例化对象
    • 元类中__init__用于实例化类
"""
模拟做到底层代码 类的首字母必须要大写
"""
class MyMetaClass(type):
    def __init__(self, what, bases=None, dict=None):
        print('what', what)  # 类名
        print('bases', bases)  # 类的父类
        print('dict', dict)  # 类的名称空间
        if not what.istitle():
            raise Exception('首字母必须大写 你会不会写python 面向对象学过吗 lowB')
        super().__init__(what, bases, dict)
=========================================
#  如果想要切换产生类的元类  必须要使用metaclass 申明
class Myclass(metaclass=MyMetaClass):
    pass
=========================================
class aaa(metaclass=MyMetaClass):
    pass
# 类名首字母没有大写 报错

元类进阶

  • 元类可以控制类的产生过程,也可以控制对象!!!
    • 1.对象加括号执行产生该对象类里面的双下call
    • 2.类加括号执行产生该类的元类里面的双下call
class MyClass:
    def __call__(self, *args, **kwargs):
        pass

obj = MyClass()
obj()  # 对象加括号执行产生该对象类里面的双下call 
========================================
class MyMetaClass(type):
    def __call__(self, *args, **kwargs):
        pass
class MyClass(metaclass=MyMetaClass):
    pass

obj = MyClass()  # 类加括号执行产生该类的元类里面的双下call

# 需求:
		实例化对象 所有的参数都必须采用关键字参数的形式
    
class MyMetaClass(type):
    def __call__(self, *args, **kwargs):
        print('__call__')
        if args:
            raise Exception('必须用关键字参数传参')
        super().__call__(*args, **kwargs)


class MyClass(metaclass=MyMetaClass):
    def __init__(self, name, age):
        self.name = name
        self.age = age
        print('__init__')

obj = MyClass('jason', 18)  # 报错 必须用关键字参数传参')【原因:控制添加了__call__的判断条件】

__call__  
obj = MyClass(name='jason', age=18)  # __call__  __init__【先走双下call 在双下init】

  • 总结:
    • 如果我们想高度定制对象的产生过程
      • 可以操作元类里面的__call__
    • 如果我们想高度定制类的产生过程
      • 可以操作元类里面的__init__

双下new方法

"""
类产生对象的步骤
	1.产生一个空对象
	2.自动触发__init__方法实例化对象
	3.返回实例化好的对象
"""
__new__方法专门用于产生空对象	    骨架
__init__方法专门用于给对象添加属性    血肉

标签:总结,__,obj,name,self,本周,内容,print,def
来源: https://www.cnblogs.com/55wym/p/16538571.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有