首页 > 技术文章 > Python笔记day20-面向对象

wlswang 2019-08-06 19:28 原文

面向对象

1 装饰器

1.1 装饰器是什么?

  • 定义:装饰器是用于拓展原函数功能的一种语法,返回新函数替换旧函数

  • 作用:在不更改原函数代码的前提下,拓展出新功能

  • 语法:加上@符号,系统会自动把下面的函数当成参数传递装饰器中,从下到上

    @符又被称作语法糖

1.2 装饰器

  1. 普通装饰器
  2. 装饰带有参数的函数
  3. 装饰带有返回值的函数
  4. 带有参数的装饰器(不同函数不同效果)
  5. 把类作为装饰器(后面讲)
  6. 装饰类(后面讲)
@func1()
def func():
	print('aaa')
    #不影响原有函数的功能的同时添加新功能
# 普通装饰器 zsq(bzs)
# 装饰器带有参数 dczsq(cs='man')(bzs)()
# 被装饰的函数带参数:只需要在最内部函数传入参数即可

2 面向对象 (Object Oriented) 简称OO

  • 优势:能够实现良好编程结构,方便开发和管理
  • 高内聚(具有关联功能,总结在一个结构中)
  • 低耦合(没有关联的功能,放在不同的结构中)

2.1 面向对象相关术语

  • OO 面向对象
  • OOP 面向对象的开发 Object Oriented Programming
  • OOA 面向对象的分析 Object Oriented Analysis
  • OOD 面向对象的设计 Object Oriented Design
  • OOI 面向对象的实现 Object Oriented Implementation
  • OOA->OOD->OOI 面向对象程序开发的顺序(OOP)

2.2 类和对象

  • 类:类就是抽象的概念而已

    • 类似一个实物特征的集合,是一个抽象的名次概念
    • 没有具体实施的行为也是一个类,抽象的概念,存在脑海中
  • 对象:对象是具体的实物或行为

    • 对象是真实存在的实物,看得见摸得着,不用想象
    • 真实发生的行为也是对象,不用想象
  • 类和对象的关系

    都是面向对象中必不可少的内容

    • 类是对象的抽象;类是由对象总结来的。这个过程叫做抽象化
    • 对象的类的实例;对象是由类具体实施而来。这个过程叫做实例化

2.3 类和对象的实现和书写

创建文件:
习惯使用小写字母当作文件名,可以使用下划线进行分割。

类名的书写规范:
使用驼峰命名法,
大驼峰: MyCar XiaoBaiTu...
小驼峰: myCar xiaoBaiTu...

类的组成:
女朋友:(类)
特征:性别女,肤白...
sex = "女"
color = "皮肤白"
...

    功能:洗衣,做饭,打扫卫生...
        def wash():
            洗衣功能

        def cook():
            做饭功能


类中内容只有2个:属性和方法
    属性:用于描述特征的变量->成员属性
    方法:用于描述功能的函数->成员方法
    也有一种说法:类中只有属性

类的书写规则:
1.声明一个类必须使用class关键字
2.类名的声明需要符合驼峰命名法(推荐)
3.类中只能存在两种内容:属性和方法,除此之外不允许出现其他内容
4.声明成员属性的时候,变量必须赋值
5.声明成员方法的时候,按照函数声明规范即可(参数中会自动添加self参数)

实例化对象:
对象变量 = 类名()

python中有无数个数据类型,本质上所有的数据类型都是一个类

2.4 类和对象成员的操作

  • 获取类和对象中所属成员的信息

  • __dict__查看类和对象里面的属性和方法

    以字典的形式展现出来,对象默认是空字典

    • 类:类名.__dict__
    • 对象:对象名.__dict__

2.4.1 类成员的操作

  • 成员属性(相当于变量)
    • 访问 类名.成员属性名
    • 修改 类名.成员属性名 = 新值
    • 删除 del 类名.成员属性名
    • 添加 类名.成员属性名 = 值
  • 成员方法 (相当于函数)
    • 访问 类名.方法名(参数)
    • 修改 类名.方法名 = 新的函数 (注意:不要加括号) (不推荐使用)
    • 删除 del 类名.方法名 (注意:不要加括号)
    • 添加 类名.新方法名 = 函数(也可以是lambda表达式)(不推荐使用)

2.4.2 对象成员的操作

  • 成员属性
    • 访问 对象.成员属性名
    • 修改 对象.新成员属性名 = 新值
    • 删除 del 对象.成员属性名 (必须是属于当前对象的成员才可以)
    • 添加 对象.成员属性名 = 值
  • 成员方法 (相当于函数)
    • 访问 对象.成员方法名()
    • 修改 对象.成员方法名 = 新的函数 (注意:不要加括号)
    • 删除 del 对象.成员方法名 (注意:不要加括号)
    • 添加 对象.成员方法名 = 函数(注意:不要加括号)

注意事项:

  1. 实例化对象的时候通常情况下类中的成员不会在对象中复制一份
  2. 访问对象成员的时候,只要没有该成员,对象会向实例化它的类查找
  3. 对象成员的添加和修改,都只会影响当前对象本身,不会影响类和其他独享
  4. 删除对象成员时候,必须是该对象自身的成员才可以删除,不可以删除类和其他对象

2.5 self

  1. 只是一个参数。

  2. 在对象使用方法的时候,当前对象会作为第一个参数的实参传入

  3. self相当于语言中的代词,表示当前对象本身(其他语言中也有使用this)

  4. self的作用连接整个对象的所有信息。桥梁的作用!

  5. self不是关键字,只是一个参数变量名而已,可以使用其他单词代替(禁止代替)

  6. 方法的初步分类:
    方法中具体接受对象的参数这个方法,叫做非绑定类的方法

    ​ 方法中没有接受对象的参数这个方法,叫做绑定类的方法

    class Fathe:
        # 非绑定类
    	def eat(self):
    		pass
        # 绑定类
        def run():
            pass
    

2.6 封装、继承和多态

2.6.1 封装

  • 特征:就是对于成员操作进行限制的保护性措施!

  • 方法:__属性 __方法

  • 封装分三个级别:

    • 私有化封装 private (注意:只能在类中访问)

      • 方法:在成员属性或者方法名称之前,添加双下划线,就可以私有化成员
      • 特征:私有化的成员属性和方法,无法在类/对象的外部访问;私有化成员仅允许在类/对象的内部访问
      # 私有化的封装
      # 私有化的封装只能在类内部访问,不能在外部访问
      # 私有化封装,就是在属性/方法名前加两个下划线__
      class Human():
           # 成员属性
           name = "人类"
           __girlfriend = "女朋友"
           # 成员方法
           def move(self):
               print(self.__girlfriend)
               self.__love()
               print("人类使用交")
           # def love(self):
           def __love(self):
               print("开心的造人")
       shuaige = Human()
       shuaige.move()
      #在外部不管是用类还是对象访问都不可以
      # print(shuaige.__girlfriend)
      # print(Human.girlfriend) # 这个也访问不到,因为没在类内访问
      # 调用成员方法
      # shuaige.love()
      
    • 受保护的封装 protected

      • 名称前加 _ 加一个下划线即可
      • 访问的效果和公共的封装式一样,但不要随便访问。
      • 受保护的目的:仅仅允许成员在当前类/对象或者子类/子对象中访问,外部禁止访问。
      类/对象内 类/对象外 子类/子对象 类/对象外
      私有化 × ×
      受保护 √(python)不支持受保护的封装,请不要在外部访问
      公共的

      注意:python目前不支持设定受保护的成员,但是,是开发者约定的使用方式

    • 公共的封装 public

      • 方法:任何成员在没有进行私有化操作的时候默认都是公共的封装。
      • 特征:公共的封装成员属性和方法,在任何位置都可以被访问。
  • 强行的访问私有化的属性(不建议强行访问,职业操守问题)

    # python采用了改名策略来实现的私有化封装
    #python本身属于完全开源免费的,所以无法做到真正的私有化封装
    class Human():
        # 成员属性
        name = "人类"
        __girlfriend = "女朋友"
        # 成员方法
        def move(self):
            print(self.__girlfriend)
            self.__love()
            print("人类使用交通工具")
        def __love(self):
            print("开心的造人")
    shuaige = Human()
    
    # 改名格式:_类名__属性名/方法名
    #私有化封装不建议强行访问,职业操守问题
    print(shuaige._Human__girlfriend)
    shuaige._Human__love()
    

2.6.2 继承

  • 定义:面向对象中的继承就是表示一个类获取另外一个类某个或多个成员的操作。

  • 意义:提高代码的重用率,建立新的类与类的关系,方便其他的逻辑操作。

  • 父类(基类或超类):被其他类继承的类

  • 子类(派生类):继承其他类的类

  • 格式:

    class 父类:
    	pass
    class 子类(父类):
        pass
    
  • 特征:

    • 在不指定父类的情况下,所有的类均继承自object类(系统提供的)

    • 子类继承父类就具有父类的所有成员。

    • 子类继承父类,不会将父类成员复制到子类中,子类如果需要成员,可以找父类索取。

    • 私有化的成员,允许在子类中单独建立一份,不会找到父类索取私有成员

    • 子类可以根据需求添加自己独有的成员类进行操作

    • 子类重载父类的成员。仅仅是对子类/对象有效。并不会影响父类。

    • 子类在重载父类的方法的时候,也可以调用父类的方法来进行操作

      父类名().父类方法名()  # ---> 任何方式都允许 (菱形继承会出现bug)
      super().父类方法名() # ---->必须是带有self的对象方法才可以很实用
      
  • 单继承和多继承

    • 单继承

      # 一个类只能继承一个父类的方式
      父类:
      	pass
      子类(父类):
          pass
      
    • 多继承

      父类1:
          pass
      父类2:
          pass
      父类3:
          pass
      子类(父类1,父类2,父类3):
          pass
      

      多继承的问题所在:菱形继承或者钻石继承中的问题。
      菱形bug:某个方法在继承中被多次调用!

      菱形继承的bug解决:MRO列表和super 类
      解决办法:将菱形继承改变成类似于单继承的方式
      
      当我们定义一个菱形继承关系的时候,程序会自动生成一个新的MRO列表。
      
      MRO列表: Method Realtion Order  方法关系列表。
      
      MRO列表的生成的原则:1.子类永远在父类的前面  2.同一等级的类,按照子类中的继承顺序摆放
      
      super()调用的时候,不是查找父类, 实际上super是查找MRO列表的上一个类
      
      super()调用对象方法的时候不需要传入对象,自动传入
      
      issubclass(子类,父类) :检测一个类是否是另一个类的子类
      
      如果需要查看mro列表,可以使用类名.mro() 方法 或用类名.__mro__ 查看
      
      继承问题:  python3里面是新式类,以广度优先
      

2.6.3 多态

  • 定义:不同的子类对象调用相同的父类方法,产生不同的执行效果。
  • 多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承)
  • 多态是调用方法的技巧,不会影响到类的内部设计
  • 关键点:继承 改写(重载,重写)
# 定义狗类
class Dog:
	def work(self):
		print("狗是人类的好朋友")
# 定义警犬类
class ArmyDog(Dog):
	def work(self):
		print('追击敌人')
# 定义缉毒犬类
class DrugDog(Dog):
	def work(self):
		print('追查毒品')
# 定义二哈类
class HaDog(Dog):
	def work(self):
		print("欢乐的破坏")
#定义人类
class Person:
	def with_dog(self, dog):  # 只要能接收父类对象,就能接收子类对象
		dog.work()  # 只要父类对象能工作,子类对象就能工作。并且不同子类会产生不同的执行效果。
p = Person()
p.with_dog(ArmyDog())
p.with_dog(DrugDog())
p.with_dog(HaDog())

2.7 面向对象常用函数

  • issubclass()
    作用:检测一个类是否是另一个类的子类
    格式:issubclass(子类,父类)
    返回值:布尔值
    注意事项:只要存在于继承关系中 就成立

  • isinstance()
    作用:检测一个对象是否是指定类的实例
    格式:isinstance(对象,类)
    返回值:布尔值

  • hasattr()

    ​ 作用:检测类/对象是否包含指定名称的成员
    ​ 格式:hasattr(对象,'成员名称')
    ​ 返回值:布尔值
    ​ 注意:可以检测类也可以检测对象,只要可以访问就算存在

  • getattr()
    作用:获取类.对象的成员值
    格式:getattr(对象,'成员名称',默认值)
    返回值:不确定

  • setattr()
    作用:设置类/对象的成员属性值
    格式:setattr(对象,'成员名称',设置的值)
    返回值:无

  • delattr()
    作用:删除类/对象的成员
    格式:delattr(对象,'成员名称')
    返回值:无

  • dir()
    作用:获取对象可以访问的所有成员的列表
    格式:dir(对象)
    返回值:对象可以访问的所有成员的列表

  • property() 后面讲
    作用:设置描述符操作的函数

3 内置模块、成员、方法分类

3.1 内置成员

3.1.1 _dict_ : 获取类/对象的所属成员组成的集合 *


3.1.2 _doc_ : 获取类的文档信息 *

class Human():
    '''
    这是一个类文档
    '''
    name = "wls"
print(Human.__doc__) # 这是一个类文档

3.1.3 _name_ : 获取类名称的字符串

s = list.__name__
print(s,type(s)) # list <class 'str'>

3.1.4_module_:获取当前类所在的文件的名称,如果是当前文件,显示为main(后面会讲其他的)

class Human():
    pass
print(Human.__module__)
# __main__ 当前文件的意思

3.1.5_bases_:获取当前类的父类元组

class A():
    pass
class A1():
    pass
class A2():
    pass
class A12(A1,A2):
    pass
print(A12.__bases__,type(A12.__bases__))
# (<class '__main__.A1'>, <class '__main__.A2'>) <class 'tuple'>

3.2 方法的分类

3.2.1 对象方法

# 该方法中会直接传入当前对象
# 调用方式:对象.成员方法()
# 特征:会将当前对象传入方法中 

3.2.2 类方法

# 在方法中会直接传入当前类
# @classmethod: 不需要实例化,直接类名.方法名()来调用。
# 调用方式:类.成员方法()
# 特征:会将当前类传入方法中

3.2.3 绑定类的方法

# 在方法中不会传入类或者对象的方法
# 调用方式:类.成员方法()
# 特征:不会传入类或者对象,只能通过类来调用

3.2.4 静态方法

# 与类和对象无关,但是存在于类结构中的方法
# @staticmethod: 返回函数的静态方法。
# 调用方式:类.成员方法() 或者对象.成员方法()
# 特征:类或者对象都可以调用

3.3 内置模块

3.3.1 压缩模块-zipfile (后缀为zip)

import zipfile   ZipFile 大驼峰命名   zipFile 小驼峰命名
#zipfile.ZipFile(file[, mode[, compression[, allowZip64]]])
#ZipFile(路径包名,模式,压缩or打包,可选allowZip64)
#功能:创建一个ZipFile对象,表示一个zip文件.
#参数:
    -参数file表示文件的路径或类文件对象(file-like object)
    -参数mode指示打开zip文件的模式,默认值为r
        r    表示读取已经存在的zip文件  (鸡肋 
        w    表示新建一个zip文档或覆盖一个已经存在的zip文档 
        a    表示将数据追加到一个现存的zip文档中。
    -参数compression表示在写zip文档时使用的压缩方法
        zipfile.ZIP_STORED      只是存储模式,不会对文件进行压缩,这个是默认值
        zipfile.ZIP_DEFLATED    对文件进行压缩 
    -如果要操作的zip文件大小超过2G,应该将allowZip64设置为True。

#压缩文件
#1.ZipFile()          写模式w打开或者新建压缩文件
#2.write(路径,别名)   向压缩文件中添加文件内容
#3.close()            关闭压缩文件

#解压文件
#1.ZipFile()             读模式r打开压缩文件
#2.extractall(路径)      解压所有文件到某个路径下
#  extract(文件,路径)    解压指定的某个文件到某个路径下
#3.close()               关闭压缩文件

3.3.2 压缩模块-tarfile(后缀为.tar | .tar.gz | .tar.bz2)

#bz2模式的压缩文件较小 gz相对bz2大
w      单纯的套一个后缀 打包
w:bz2  采用bz2算法 压缩    
w:gz   采用gz算法 压缩
with tarfile.open(路径,"w:bz2") as tf:
	tf.add()
	
#压缩文件
#1.open('路径包名','模式','字符编码') 创建或者打开文件 
#2.add(路径文件,arcname="别名") 向压缩文件中添加文件
#3,close() 关闭文件

#解压文件
#1.open('路径包名','模式','字符编码') 读模式打开文件 
#2.extractall(路径)      解压所有文件到某个路径下
#  extract(文件,路径)    解压指定的某个文件到某个路径下
#3.close()               关闭压缩文件
  with tarfile.open(路径1,"r") as tf:
      tf.extractall(路径2)
#追加文件
open()                   追加模式 a: 打开压缩文件 正常添加即可
  with tarfile.open(文件,"a") as tf:
      tf.add("路径","别名")

#查看压缩包中的内容
getnames()    

3.3.3 时间模块

time()      获取本地时间戳 *
	格式:time.time()
	参数:无
	返回值:时间戳
ctime()     获取本地时间字符串(参数是时间戳,默认当前)
	格式:time.ctime(时间戳)
	参数:时间戳
	返回值:字符串
localtime()     获取本地时间元组          (参数是时间戳,默认当前) *
	格式:time.localtime(时间戳)
	参数:时间戳
	返回值:时间元组
mktime()        通过时间元组获取时间戳    (参数是时间元组)
	格式:time.mktime(时间元组)
	参数:时间元组
	返回值:时间戳
asctime()       通过时间元组获取时间字符串(参数是时间元组)
	格式:time.asctime(时间元组)
	参数:时间元组
	返回值:字符串
sleep()         程序睡眠等待  *
	格式:time.sleep(秒数)
	参数:数值
	返回值:无
strftime()      格式化时间字符串(格式化字符串,时间元祖)   *
	格式:time.strftime(格式化字符串,时间元组)
	参数:格式化字符串,时间元组(可选)
	返回值:字符串
	注意:第二个参数如果不选,则默认为当前时间
	#参数1
    ft = time.strftime('{today}%Y{y}%m{m}%d{d} %H{h}%M{f}%S{s}').format(today="今天		是:",y='年',m='月',d='日',h='时',f='分',s='秒')
    #参数2
    t = (2019,3,22,9,20,38,4,80,0)
    ti = time.strftime("%b %d %Y %H:%M%S",t)
#strptime()      将时间字符串通过指定格式提取到时间元组中(时间字符串,格式化字符串)  *
	格式:time.strptime(时间字符串,格式化字符串)
	参数:时间字符串,格式化字符串
	返回值:时间元组
	ntime = time.strptime("2018 2 5 23 56 26","%Y %m %d %H %M %S")
#perf_counter()  用于计算程序运行的时间   *
	格式:time.perfcounter()
	参数:无
	返回值:计数器的值(以分秒为单位)

3.3.4 时间模块相关知识

# 时间戳指从1970年1月1日0时0分0秒到指定时间之间的秒数,时间戳是秒,可以使用到2038年的某一天
# UTC时间: 世界约定的时间表示方式,世界统一时间格式,世界协调时间!,特指格林尼治天文台所在位置的时间也叫做格林尼治时间。中国的时区是东八区,和世界协调时间差了8个小时,多个八个小时
# 夏令时:  在夏令时时间状态下,时间会调块1个小时

# 时间元组是使用元祖格式表示时间的一种方式,他是time模块操作时间的主要方式。
    格式1(自定义):
        (年,月,日,时,分,秒,周几,一年中的第几天,是否是夏令时时间)
    格式2(系统提供):
        (tm_year = 年,tm_month = 月,tm_day = 日,tm _hour = 时, tm_min = 分, tm _sec = 秒, tm _wday = 周几, tm _yday = 一年中的第几天,tm_isdst = 是否是夏令时时间)

        0   年   4位数完整年份    四位数1997
        1   月   1-12月           1 - 12
        2   日   1-31天           1 - 31
        3   时   0-23时           0 - 23
        4   分   0-59分           0 - 59
        5   秒   0-61秒           0 - 61 #闰秒
        6   周几 周一-周天         0 - 6
        7   年中第几天    共366天  1 - 366
        8   夏令时  两种           0,1  0是  其他都不是  

#strftime()格式化时间字符串:
    格式    含义        
    %a    本地(locale)简化星期名称
    %A    本地完整星期名称
    %b    本地简化月份名称
    %B    本地完整月份名称
    %c    本地相应的日期和时间表示
    %d    一个月中的第几天(01 - 31)
    %H    一天中的第几个小时(24 小时制,00 - 23)
    %I    一天中的第几个小时(12 小时制,01 - 12)
    %j    一年中的第几天(001 - 366)
    %m    月份(01 - 12)
    %M    分钟数(00 - 59)
    %p    本地 am 或者 pm 的相应符    
    %S    秒(01 - 61)   
    %U    一年中的星期数(00 - 53 星期天是一个星期的开始)第一个星期天之前的所有天数都放在第 0 周    
    %w    一个星期中的第几天(0 - 6,0 是星期天)   
    %W    和 %U 基本相同,不同的是 %W 以星期一为一个星期的开始
    %X    本地相应时间
    %y    去掉世纪的年份(00 - 99)
    %Y    完整的年份
    %z    用 +HHMM 或 -HHMM 表示距离格林威治的时区偏移(H 代表十进制的小时数,M 代表十进制的分钟数)
例:

3.3.5 日历模块-calendar(了解内容)

#--calendar 日历模块 import calendar
#calendar() 获取指定年份的日历字符串 (年份,w日期间的宽度,l日期间的高度,c月份间的间距,m一行显示几个月)
calendar.calendar(2018,w=2,l=2,c=20,m=1)

#month() 获取指定年月的日历字符串 (年份,月份,w日期之间的宽度,l日期之间的高度)
calendar.month(2018,9,w = 2,l = 2)

#monthcalendar() 获取指定年月的信息列表 (年份,月份) 0从周一开始排
calendar.monthcalendar(2018,9)

#isleap() 检测是否是润年(能被4整除不能被100整除或能被400整除)
calendar.isleap(2004)

#leapdays() 指定从某年到某年范围内的润年个数
calendar.leapdays(1970,2038)

#monthrange() 获取某年某月的信息 周一是0
calendar.monthrange(2018,8)

#weekday() 指定某年某月某日是星期几
calendar.weekday(2018,8,18)

#timegm() 将时间元组转化为时间戳
ttp = (2018,10,1,13,23,34,0,0,0)
calendar.timegm(ttp)

4 魔术方法(魔法函数)

  • 一种特殊的方法
  • 特点:不需要人工调用,在特定的时刻自动执行。

4.1 __init__ 初始化方法 ****

  1. 触发时机 : 实例化对象之后触发
  2. 作用 : 为实例化的对象添加对象的所属成员。
  3. 参数 : 一个 self 接受当前对象 其他的参数根据实例化的传参决定
  4. 返回值 : 无
  5. 注意事项 : 无
 # 在创建实例对象后就触发;子类继承父类,再创建对象,也会被触发
class Human():
    def __init__(self,head):
        self.head = head
        print("被触发了")
class Man(Human):
    pass
h = Human("gair") # 创建一个对象,初始化对象
print("==")
m = Man()
# 被触发了
# ==
# 被触发了
# 当在 __init__里面设置属性时,创建的对象里面会有属性的值,而类中没有
class Human1():
    def __init__(self):
        self.name = "姓名"
        self.age = "年龄"
        print("被触发了")
class Man1(Human1):
    pass
h1 = Human1() # 建立h1对象,触发__init__方法
print(h1.__dict__) # 有__init__方法中的属性值{'name': '姓名', 'age': '年龄'}
print(Human1.__dict__) # 没有 __init__方法中的属性值
print("==")
m1 = Man1() # Man1继承了Human1 ,在建立对象时,也会触发父类__init__方法
print(m1.__dict__) # 有__init__方法中的属性值{'name': '姓名', 'age': '年龄'}
print(Man1.__dict__)# 没有 __init__方法中的属性值

4.2 _new_ 构造方法 ****

  1. 触发时机 : 实例化对象的时候触发(在 __init__之前)

  2. 作用 : 管理控制对象的生成过程。

  3. 参数 : 一个cls接受当前类,其他的参数根据实例化的参数决定

  4. 返回值 : 可有可无 没有返回值,实例化结果为 None

  5. 注意事项 : new 魔术方法跟 init 的魔术方法的参数一致(除了第一个)

    生成对象用 return object._new_(cls)

#就相当于有 __new__ 方法时,必须先执行 __new__ 方法,并且成功返回,才会初始化 __init__方法
class Human():
    def __init__(self):
        self.name = "wls"
        print("init_run")
    def __new__(cls):
        print(cls)
        print("new_run")
        return object().__new__(cls)
        # return super().__new__(cls)
a = Human()
print(a)
print(a.__dict__) # {'name': 'wls'}

a1 = Human()
print(a1)
print(a1.__dict__) # {'name': 'wls'}
a1.sex = "男"
print(a.__dict__) # {'name': 'wls'}
print(a1.__dict__) # {'name': 'wls', 'sex': '男'}
class Human():
    pass
class Man(Human):
    def __new__(cls,sex):
        if sex != "人妖":
            # .__new__(cls)这个是固定格式,前面的父类不是固定格式,可以随便定义
            # 制造的对象是什么值,由return决定
            return Human.__new__(cls)

yemen = Man("人妖")
print(yemen) # None
yemen = Man("yemen")
print(yemen)

4.2.1 单例设计模式

  1. 单例设计模式,无论有多少个实例化对象,有且只有一个对象(使用__new__模式方法)
  2. 单例模式形成后,成员属性不变,成员属性的值改变
# 单例设计模式,无论有多少个实例化对象,有且只有一个对象。它都指向同一个,共同操作,(可以理解为:QQ中的云文档,可以多人同时编辑一个文档)
class Father():
    name = "admin"
    def __new__(cls):
        if cls.name == "admin":
            cls.name = object.__new__(cls)
        return cls.name
user1 = Father() # 对象1
print(user1)
user2 = Father() # 对象2 
print(user2)
user2.sex = "男" # 其中一个对象增加一个属性,这样无论哪一个对象都可以有这个属性,他们指向同一个类
print(user1.__dict__) # {'sex': '男'}
print(user2.__dict__) # {'sex': '男'}

4.3 _del_ 析构方法 ****

Python的垃圾回收机制的实际应用

  1. 触发时机:对象被系统回收的时候自动触发

    • 页面执行完毕回收所有变量

    • 所有类的对象(引用)都被del的时候,该类就会被系统从内存中删除,注意是所有的引用都被删除哦,而不是每一次删除

  2. 作用:回收程序使用过程的信息和变量等

  3. 参数:一个self接收当前对象

  4. 返回值:无

  5. 注意事项:无

class Robat():
    def __del__(self):
        print("del running...")
r = Robat()
print("hello robat")

r1 = r # 赋值
del r
print("="*20)+-------
print(r1)
# print("结束") # 放在前面打印和放在后面打印,结果不一样
del r1
print('结束')

4.4 _call_

  1. 触发时机:将对象当作函数调用的时候自动触发
  2. 作用:常用于归结类/对象的操作步骤,方便后期调用
  3. 参数:一个self接受当前对象,其余的参数根据需求添加
  4. 返回值:可以有,可以没有
  5. 注意事项:无
class Computer():
    name = "电脑"
    def end(self):
        print("工作完成了")
    def __call__(self,sex):
        print(sex)
        self.run()
        self.end()
        return "打完收工"
    def run(self):
        print("开始工作")
# 实例化
huipu = Computer()
res = huipu("此电脑寿命2年") # 将对象当做函数
print(res)
# 此电脑寿命2年
# 开始工作
# 工作完成了
# 打完收工

4.5 _len_

  1. 触发时机:使用len函数检测对象的时候自动触发
  2. 作用: 使得len可以检测对象中某个数据的信息
  3. 参数: 一个self接受当前对象
  4. 返回值:必须有, 必须是整型(返回值就是检测的值)
  5. 注意事项:len检测什么由程序员自己决定
class Robat():
    def __init__(self):
        self.name = "hello"

    def __len__(self):
        res = len(self.name)
        return res
# 实例化类
r = Robat()
res = len(r)
print(res) # 5

4.6 _str_

  1. 触发时机:使用 print 打印对象的时候自动触发,使用 str() 转换对象的时候也会触发
  2. 作用:可以定义打印对象显示的信息内容
  3. 参数:一个self接受当前对象
  4. 返回值:必须有,且必须是字符串类型(即显示的对象内容)
  5. 注意事项:无
class Robat():
    def __init__(self):
        self.name = "hello robat"
    def __str__(self):
        print("str running...")
        return "这是一个实例化Robat得到的对象"
#实例化类
r = Robat()
print(r) # 这是一个实例化Robat得到的对象

# r.age = 19
# print(r.__dict__) # {'name': 'hello robat', 'age': 19}
# str(r)

4.7 _repr_

  1. 触发时机:在使用repr转换对象的时候自动触发
  2. 作用:可以设置repr函数操作对象的结果
  3. 参数:一个self接受有当前对象本身
  4. 返回值:有必须是字符串
  5. 注意事项:正常情况下,类中的__str__和__repr__魔术方法是完全一样的(字符串中的str和repr魔术方法就不一样。)
  6. repr():将对象转化为供解释器读取的形式。
  7. repr函数和str函数除了对字符串的处理,其他的是一致的.在类中定义魔术方法的时候想使用repr方法就得定义str魔术方法
class Robat():
    def __init__(self):
        self.name = "xiaoai"
    def __repr__(self):
        print("repr running...")
        return "这是实例化Robat得到的对象"
#实例化
r = Robat()
res = repr(r)
print(res)
# repr running...
# 这是实例化Robat得到的对象
list1 = [1,2,3,4,5]
print(str(list1),type(str(list1)))
print(repr(list1),type(repr(list1)))
# [1, 2, 3, 4, 5] <class 'str'>
# [1, 2, 3, 4, 5] <class 'str'>

str1 = "今天是个好\n日子\t七夕"
print(str(str1),type(str(str1)))
print(repr(str1),type(repr(str1)))
# 今天是个好
# 日子	七夕 <class 'str'>
# '今天是个好\n日子\t七夕' <class 'str'>

4.8 _bool_

  1. 触发时机:使用bool()转换对象的时候自动触发(一般情况下,对象转换的结果默认就是True)
  2. 作用: 用于控制检测对象成员的信息
  3. 参数: 一个self接受当前对象
  4. 返回值:必须有且必须是布尔值
  5. 注意事项:无
class Robat():
    def __init__(self, age):
        self.name = "hello"
        self.age = age
    def __bool__(self):
        print("bool running...")
        if self.age == "0":
            return True
        else:
            return False
# 实例化类
r = Robat("0")
res = bool(r)
print(res)
# bool running...
# True

5 与属性相关的常用魔术方法:

  • 就是获取成员,删除成员,修改成员相关联的魔术方法

  • 属性访问的顺序 :

    1. 调用_getattribute_
    2. 【调用数据描述符】
    3. 调用当前对象的所属成员
    4. 调用类的所属成员
    5. 【调用非数据描述符】
    6. 调用父类的所属成员
    7. 调用_getattr_

注意:以上步骤是调用某个成员的访问顺序及优先级。前面的能获取成员,就不会向后查找

5.1 _getattribute_

  1. 触发时机:访问对象成员的时候就会触发,无论成员是否存在
  2. 作用:可以在用户获取数据的时候进行数据处理等操作
  3. 参数:一个self接受当前对象,另外一个参数接受访问对象成员名称的字符串
  4. 返回值:有 不设定返回None,return的值即是处理后的对象成员值
  5. 注意事项:在当前魔术方法中禁止使用 当前对象.成员 的方式访问成员,会触发递归操作,必须借助object的__getattribute__来获取当前对象的成员
class Human:
  #添加成员属性(加入对象)
  def __init__(self):
      self.name = '东方不败'
  #添加魔术方法__getattribute__
  def __getattribute__(self, val):#val接受的是访问成员的名称字符串
      #一定不能使用当前对象.成员访问,会再次触发当前魔术方法进入递归循环!
      result = object.__getattribute__(self,val)
      #隐藏用户名
      newname = result[0] + '*' + result[-1]
      #返回数据
      return  newname
# 实例化对象
ls = Human()
# 访问对象的名称
print(ls.name)
# 东*败
class Na():
    def __init__(self):
        self.name = "laoganma"
        self.age = 60
    def __getattribute__(self, item):
        if item == "name":
            res = super().__getattribute__(item)
            print(res) # laoganma
            return res[0] + "*" + res[-1]
        else:
            return super().__getattribute__(item)
lao = Na()
print(lao.name)
print(lao.age)
# l*a
# 60

5.2 _getattr_ *****

  1. 触发时机:访问不存在的对象成员的时候自动触发
  2. 作用:防止访问不存在成员的时候报错,为不存在的成员定义值(return的值)
  3. 参数:一个self接受当前对象,第二个参数接受访问成员的名称字符串
  4. 返回值:可有可无
  5. 注意事项 : 无
class Human:
    def __init__(self):
        self.name = '东方不败'
    def __getattr__(self,val):
        #检测用于访问与name相关的信息都返回名称 (name)
        print("getattr running...")
        if 'name' in val:
            return self.name
#实例化对象
df = Human()
print(df.name)
#访问对象成员
print(df.name233)
# 东方不败
# getattr running...
# 东方不败

5.3_setattr_ *****

  1. 触发时机:添加对象成员或者修改对象成员的时候自动触发!
  2. 作用:可以限制或者管理对象成员的添加与修改操作
  3. 参数:一个self接受当前对象 第二个接受设置的成员名称字符串 第三个设置的值
  4. 返回值:无
  5. 注意事项:在当前魔术方法中禁止使用当前对象.成员名 = 值的方式,会触发递归操作!,必须借助须借助object的_setattr_
class Human:
	sex = '男'
	# 添加成员属性(加入对象)
	def __init__(self):
		self.name = '帅'
		self.age = 18
	#魔术方法
	def __setattr__(self,attrname,value):
		#print('setattr方法被触发')
		#禁止修改性别,
		if attrname == 'sex':
			pass
		else:
			object.__setattr__(self,attrname,value)
# 实例化对象
df = Human()
print("="*20)
#访问性别
print(df.sex)
print("="*20)
#修改性别
df.sex = '女'
print("="*20)
print(df.sex)
# setattr 方法触发
# setattr 方法触发
# ====================
# nan
# ====================
# setattr 方法触发
# ====================
# nan

5.4_delattr_

  1. 触发时机:删除对象成员的时候自动触发
  2. 作用:1.可以限制对象成员的删除 2.删除不存在的属性时防止报错
  3. 参数:一个self接受当前对象,另外一个接受删除的成员属性名称的字符串
  4. 返回值:无
  5. 注意事项:如果要删除对象成员,要借助object中的__delattr__来操作。
class Human():
    name = "刘胡兰"
    # 初始化魔术方法
    def __init__(self):
        self.sex = "女"
        self.age = "15"
    # 魔术方法
    def __delattr__(self,value): #第二个参数被删除的对象成员属性名称
        print("dclattr running...")
        if value in self.__dict__: #如果成员存在对象中则删除
            object.__delattr__(self,value)
        else: # 不存在不删除,也不报错
            pass
hl = Human()
# print(hl.__dict__)
hl.page = "一页"
print(hl.__dict__)
del hl.page
print(hl.__dict__)
# {'sex': '女', 'age': '15', 'page': '一页'}
# dclattr running...
# {'sex': '女', 'age': '15'}
class Myself():
    def __init__(self):
        self.name = "wls init"
        self.girl = "ssddsss"
    def __delattr__(self, item):
        print("delattr running...")
        if item == "name":
            pass
        else:
            object.__delattr__(self,item)
w = Myself()
print(w.__dict__)
del w.girl
print(w.__dict__)
# {'name': 'wls init', 'girl': 'ssddsss'}
# delattr running...
# {'name': 'wls init'}

推荐阅读