第七周 内容回顾

第七周 内容回顾

1、面向对象

1.1、面向对象的概念

我们将数据与功能绑定到一起的操作起名为:面向对象编程
将特定的数据与特定的功能绑定到一起,将来只能彼此调用

1.2、编程思想

面向过程编程

  • 我们之前所学缩写的代码都是面向过程编程
  • 过程其实就是流程,面向过程编程其实就是执行一系列的流程
  • 按照指定的步骤依次执行,最终就可以得到想要的结果

面向对象编程

  • 核心就是对象二字
    • 对象其实就是一个容器,里面将数据和功能绑定到一起
  • 例如 游戏人物
    • 只负责创造出该人物以及该人物 具备的功能 至于 后续战绩如何 无人知晓

总结:

面向过程 编程相当于让你给出一个问题的具体解决方案
面向对象 编程 相当于让你创造出一些事物之后 不用你管

两种编程思想没有优劣之分 仅仅是使用的场景不同,甚至很多时候两者 混合使用

1.3、对象与类的概念

对象: 数据与功能的结合体

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

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

1.4、对象与类的创建

面向对象 编程 本质是将数据和功能绑定到一起 但是 为了突出 面向对象 编程形式
python 特地开发了一套语法 专门用于面向对象编程的操作

创建类的完整语法

class Myclass:
    该对象的公共数据
    该对象的公共数据
    ...

class是定义类的关键字
MyClass是类的名字
类名的命名 跟变量名一致 并且 推荐首字母大写(为了更好的区分)

类体代码: 分为公共的数据 和公共的方法
类体代码 在类定义阶段 就会执行

1.5、查看名称空间的方法

关键字: __dict__

print(Myclass.__dict__) #查看名称空间 返回的是一个字典
print(Myclass.__dict__['name']) #使用字典 的取值方式 获取名字
print(Myclass.__dict__.get('name')) #使用字典取值方式 获取名字

在面向对象 编程中 想要 获取名称空间中的名字 可以直接采用句点符 比通过__dict__字典方式取值更方便

print(Myclass.name)
print(Myclass,age)

1.6、类实例化产生对象

实例化产生对象:类名加括号

s1=Myclass()

2、对象的公共属性与独有属性

2.1、对象的公共属性

在创建类 时在类体代码中直接编写的数据 和指定定义的函数 为对象的公共属性
类体代码 在类定义阶段 就会执行

2.2、对象独有属性

2.2.1对象独有数据

关键字: __init__
双下init函数 是专门赋给对象 创建独有数据的 其他对象 不能调用>>>面向对象思想 将数据和功能整合到一起

class Myclass:
    # 独有数据
    def __init__(self,name,age,gender):
        self.name=name
        self.age=age
    # 公共数据
    gender='male'
        

实例化产生对象步骤

  1. 产生一个空对象
  2. 自动触发__init__方法实例化对象
  3. 返回实例化好的ui想
2.2.2、独有方法

针对 对象独有方法 我们无法 真正的实现

  1. 如果在全局则 不是独有的
  2. 如果 在类中 则时公共的

python 解释器针对上述问题添加了一个非常牛的特性

  • 定义在类中的函数 默认为是绑定给对象(相当于是对象独有的方法)

3、动静态方法

动静态方法算是定义在类体代码中的函数方式

动态方法

  1. 绑定给对象的方法
  2. 直接在类体代码中编写即可
  3. 对象调用会自动将对象当作第一个参数传入
  4. 类调用则有几个参数 就需要传入几个canshu
  5. 绑定给类的方法
  6. 类调用会自动将类 当作第一个参数 传入
  7. 对象调用 会自动将 对象的类当作第一个参数 传入

静态方法

  • 就是普通的函数
  • 对象或 类来调用 都必须传固定的参数个数
class Myclass:
    #绑定给对象的方法
    def eat(self):
        print('绑定给对象的方法',self)
    #绑定 给类的方法
    @classmethod
    def run(cls):
        print('绑定给类的方法',cls)
    #静态方法
    @staticmethod
    def sleep(a,b):
    	print('静态方法 谁来调用 都必须自己传参',a,b)

4、面向对象 三大特性之继承

面对想的三大特性
继承封装多态

继承的含义
在编程世界里 继承其实就是用来描述 类与类 之间的数据关系
例如: 类A 继承 类B(拥有了 类B 里面所有的数据与功能)

继承的目的
编程世界里 继承就是为了节省代码的编写
例如:可以继承一个类 也可以继承多个类

继承的操作

  1. 定义类时候 需要类名 后面加括号
  2. 括号内填写你需要继承的类名
  3. 括号内可以填写 多个父类,逗号隔开即可(可以继承多个类)
class 类名(要继承的类名):
	pass
===========================
#继承多个类
class Myclass(要继承的类名):
    pass

4.1、继承的本质

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

继承: 从上往下白嫖 各自基类里面的资源

对象、类、父类的概念规律

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

4.2、名字的查找顺序

不继承的情况下 名字的查找顺序
先从对象自身查找,如果没有的话,再去产生该对象的类中查找
对象>>>类
单继承的情况下 名字的查找顺序
先从对象自身查找,然后是产生该对象的类,然后是一个个父类
对象>>>类>>>父类

多继承的情况下 名字的查找顺序

  1. 非菱形继承

    深度优先(每个分支都走到底 再切换)

  2. 菱形继承(最后归总到一个自定义类上)

    广度优先(前几个分支都不会走到最后一个类 最后一个分支才会走到底)

可以通过类.mro()方法查看该类产生的对象名字的朝朝顺序
返回的是一个列表

只要涉及到对象的查找顺序 那么几乎都是:
对象自身 >>>类>>>父类

4.3、经典类与新式类

经典类

  • 不继承objectobject子类的类(什么都不继承)

新式类

  • 继承了objectobject的子类

在oython3 中所有的类都会默认继承object
意味着python3 里面只有新式类
由于我们在定义类的时候 如果没有想要继承的父类 一般推荐直接继承object

class Myclass(object):
    pass
  • 这样可以让 类代码兼容 python2

4.4、派生方法

派生:
子类中 定义了 与父类一摸一样的方法,比且扩展了该功能

主要关键字: super().

# 定义一个父类
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,student_id):
        #通过 super 调用父类的方法
        super().__init__(name,age,gender)
        self.student_if=student_id

5、面向对像的三大特性特性之封装

封装其实就是将数据 或 功能隐藏起来(包起来,装起来)
隐藏的目的 不是让用户 无法使用,而是给这些隐藏的数据开设特定的接口,让用户使用接口才可以去使用 我们在接口中添加一些额外的操作

在类定义阶段使用双下划线开头的名字 都是隐藏属性
后续类和对象都无法直接获取

在python中不会真正的限制任何代码
隐藏属性 如果真的需要访问,也可以 只不过需要做变形处理
__变量名>>>_类名__变量名
寂然隐藏了 就不该 使用 变形之后的名字 去访问 不然就失去了其原本的隐藏意义了

class Student(object):
    __school = '北京大学'
    def __init__(self,name,age):
        self.__name=name
        self.__age=age
    #查找封装后的数据的通道,也叫接口
    def check_info(self):
        print("""
        学生姓名:%s
        学生年龄:%s
        学校:%s
        """ % (self.__name, self.__age, self.__school))
    #创建一个 修改数据通道的 通道(接口)
    #参数中要有 要修改数据的参数
    def set_info(self,name,age,school):
        if len(name)==0:
            print('用户不能为空')
            return
        if not  isinstance(age,int):
            print('年龄,必须是数字')
            return
        self.__name= name
        self.__age = age
        self.__school = school

5.1、property伪装属性

可以简单的理解为 将方法伪装成 数据

数据与方法的使用方式
obj.name数据只需要点名字
obj,func()方法使用 还需要加括号
伪装之后 可以将func方法伪装成数据obj.func

#伪装属性
class Person:
    def __init__(self,name,weight,height):
        self.name=name
        self.weight=weight
        self.height = height
    #伪装 装饰器语法
    @property
    def BMI(self):
        return self.weigt / (self.heigt**2)
# 正常情况下 我们使用的方法 都是需要加括号
p1=Person('kk',58,1.7)
#因为加了伪装 装饰器 所以可以像数据一样直接使用
print(p1.BMI)

5.2、匿名方法

我们以查看或和修改 一个封装数据为例

class Persondef __init__(self,name.weight,height):
        self.__name = name
        self.weight = weight
        self.heigt = height
        
    # 查看封装 __name的接口
    # 将接口伪装一下,那么 在通过接口查看name数据时 可以和查看普通数据一样
    @property
    def name(self):
        return self.__name
    #将 修改的封装接口 变为 像普通 数据一样 可以直接通过=号 直接修改
    @name。setter
    def name(self,value):
        self.__name = value    

6、面向对象三大特性 之多态

多态: 一种事物的多种形态
水:液态、气态、固态
动物:人、狗、猫、猪

一种事物有多种形态,但是相同的功能应该有相同的名字
这样的话,以后我们无论 拿到哪个具体的动物,都不需要管它到底是谁 直接调用相同的功能即可

多态的概念 其实我们早就以及接触过了
例如:列表 元组 字典 在查找元素 个数的时候 我们用len方法,但他们都是不同的数据类型和结构
这显然就是将方法名称统一为了len

鸭子类型:

只要你长得像鸭子,走路像鸭子,说话像鸭子,那么你就是鸭子
你不需要继承共同的父类 当你们的阿方法 类似时,也要讲方法名称统一

操作系统
Linux(一切皆文件)
只要你能读数据,能写数据,那么你就是文件
那么不管硬盘、内存、文件、的读和取 的方法名都要相同

一切皆对象
在python世界里 所有东西都可以看成是对象
例如:

文件名、文件对象
模块名:模块对象
变量名:变量对象

7、面向对象 反射

反射: 通过字符串来操作对象的数据或方法

主要四个反射方法:

关键字功能
hasattr判断对象是否含有某个字符串对应的属性
getattr获取对象字符串对应的属性
setattr根据字符串给对象设置属性
delattr根据字符串给对象删除属性
class Student:
    school = '清华大学'
    def choice_course(self):
        print('选课')
s1=Student()

while True:
    #获取用户想要查找的名字
    user_name=input('请输入您要查找的名字>>>').strip()
    #判断 对象中是否有该属性
    if hasattr(s1,user_name):
        #获取属性值
        res=getattr(s1,user_name)
        #判断是否能调用
        if callable(res):
            #能调用 就是方法
            print('是函数',res())
            #没有则 为数据
            else:
                print('',res)
    else:
        print('不好意思 您想要的查找的名字 对象没有')

8、面向对象魔法方法

魔法方法其实就是类中定义双下方法
之所以会叫魔法方法 原因是 这些方法都是达到某个条件 自然触发的,无需调用
例如:__init__方法在给对象设置独有数据时自动触发(实例化)

魔法方法功能及触发条件
__init__实例化对象时自动触发
__str__对象在被执行打印操作时会自动触发(该方法必须返回一个字符串,返回什么字符串,打印对象之后就展示什么字符串)
__call__对象加括号调用时 自动触发该方法
__getattr__对象获取一个不存在的属性名 自动触发(该方法返回什么,对象获取不存在属性名就会得到什么)
__setattr__当对象操作属性值得时候自动触发(对象···属性名=属性值)
__del__当对象 在被删除(主动或被动)得时候自动触发
__getattribute__对象获取属性得时候自动触发 无论这个属性存不存在
__enter__对象在被with执行上下文操作的时候自动触发,该方法返回什么,as关键字后面的变量名就会得到什么
__exit__对象被with语法执行并运行完with子代码后 自动触发
__new__产生对象时自动触发,在双下init之前触发

8.1、魔法方法笔试题

题目要求:补全以下diamagnetic,执行之后不会报错

class Context# 对象被 with 语法执行上下问操作的时候 需要有双下enter方法 和双下exit方法
	def __enter__(self):
        return self
    def __exit__(self)
    	pass
    def do_something(self):
        pass
with Context()as f:
    f.do_something
    

9、元类

9.1、元类的简介

基础阶段 我们使用type来查看数据类型
但是学了面向对象之后发现查看的不是数据类型 而是该数据所属的类

我们定义的数据类型其实本质还是通过各个类产生的对象
我们也可以理解为type用于查看产生当前对象的类是谁

9.2、产生类的两种方式

通过class关键字

class Myclass:
    pass

利用元类type

type(类名,类的父类,类的名称空间)

  • 学习元类其实就是掌握类的产生过程,我们就可以在类的产生过程种 高度定制化的行为

9.3、元类的基本使用

只有继承了 type的类才能称之为元类

class Mymetaclass(type):
    pass

#如果想要切换产生类的元类 则不能使用继承,必须使用关键字 metaclass 声明
class Myclass(metaclass=Mymetaclass):
    pass

例题:规定定义类时 类名必须首字母大写

推导:
类中的__init__用于实例化对象
元类中__init__用于实例化类

#定义一个元类
class Mymetclass(type):
    #重新 制定 元类 给类名 添加一个条件判断
    def __init__(self,what,bases=Nonne,dict=None):
        if not what.istitle():
            #当定义类时 首字母不为大写 
            raise Exception('类名要大写')
        super().__init__(what,bases.dict)
#定义一个类 以我们的元类为其元类
class My_class(metaclass=Mymetclass):
    pass

#应为类名没有 每个首字母大写 所以报错

9.4、元类的进阶

元类不单单可以控制类的产生过程 其实也可以控制对象

例题
制定产生对象,实例化对象时,对象传参必须要以关键字传参的方式
思路:

  1. 我们知道 对象加括号执行产生该对象的 类里面的__call__
  2. 那么类加括号调用 也就会执行产生该类的元类里面的call
  3. 那么在生成对象时 类就进行了一个加括号调用的操作,所以只要在元类__call__中添加限制即可
class Mymetaclass(type):
    def __call__(self,*args,**lwargs):
        if args:
            raise Exception('只能进行关键字传参')
            return
        super().__call__(args,kwargs)

class Myclass(metaclass=Mymetaclass):
    def __init__(self,name,age):
        self.name=name
        self.age=age
        
s1=Myclass('kk',age=18)

#直接报错

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

9.5、双下new方法

类产生对象的步骤

1. 产生一个空对象
2. 自动触发``__init__``方法实例化对象
3. 返回实例化好的对象
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值