抽象,多态,反射,内置方法

Ⅰ 抽象

【一】什么是抽象

将某几个具体的生物,根据特征总结成一个类,逐层向上总结

# 例如:
# 唐老鸭 肉鸭 北京烤鸭 ---> 鸭子
# 北极熊 黑熊 --> 熊
# 猫 老虎 --> 猫科
# 鸭子 熊 猫科 --> 动物

【二】什么是继承

与抽象相反,自上而下解包

# 例如:
# 动物 ---> 熊 ---> 黑熊
class Animal(object):
    def speak(self):
        print(f'任何动物都能叫')
    def __init__(self,color,foot,hand):
        self.color = color
        self.foot = foot
        self.hand = hand

class BlackBear(Animal):
    def __init__(self,color,foot,hand):
        super().__init__(color,foot,hand)

bear = BlackBear('black',2,2)
print(bear.color)   # black
bear.speak()    #  任何动物都能叫

【三】抽象类

  • 与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化
  • 类是从一堆对象中抽取相同的内容而来的,那么抽象类是从一堆中抽取相同的内容而来的,内容包括数据属性和函数属性。

【四】在python中实现抽象类

导入模块

# 一切皆文件
# 利用abc模块实现抽象类
import abc
# 所有继承父类的子类必须重写父类的某些方法,这个父类就叫抽象类
import abc
import json


class Animal(metaclass=abc.ABCMeta):

    def __init__(self, color, foot, hand):
        self.color = color
        self.foot = foot
        self.hand = hand

    def speak(self):
        print(f'任何动物都能叫')

    # 在子类中必须重写父类的当前方法
    @abc.abstractmethod
    def walk(self):
        ...


class BlackBear(Animal):
    def __init__(self, color, foot, hand):
        super().__init__(color, foot, hand)

    # 如果不重写父类的方法就会报错
    # Can't instantiate abstract class BlackBear with abstract methods walk
    def walk(self):
        ...


bear = BlackBear('black', 2, 2)
print(bear.color)
bear.speak()

【五】示例

import os
class FileCheck(metaclass=abc.ABCMeta):
    def __init__(self):
        self.BASE_DIR = os.path.dirname(__file__)
        self.encoding = 'utf-8'

    @abc.abstractmethod
    def read_data(self):
        print(f'读取数据方法')
        ...

    @abc.abstractmethod
    def save_data(self):
        ...


# 文本文件处理类
class TextFileCheck(FileCheck):
    def __init__(self):
        super().__init__()
        self.file_path = os.path.join(self.BASE_DIR, 'data.text')

    def read_data(self):
        with open(file=self.file_path, mode='r', encoding=self.encoding) as fp:
            data = fp.read()
        return data

    def save_data(self):
        with open(file=self.file_path, mode='w', encoding=self.encoding) as fp:
            fp.write("你真帅!")


obj_text = TextFileCheck()
print(obj_text.read_data())


# json文件处理类
class JsonFileCheck(FileCheck):
    def __init__(self):
        super().__init__()
        self.__ensure_ascii = False
        self.file_path = os.path.join(self.BASE_DIR, 'data.json')

    def read_data(self):
        with open(file=self.file_path, mode='r', encoding=self.encoding) as fp:
            data = json.load(fp=fp)
        return data

    def save_data(self):
        with open(file=self.file_path, mode='w', encoding=self.encoding) as fp:
            json.dump(obj={'username': "dream"}, fp=fp, ensure_ascii=self.__ensure_ascii)


json_obj = JsonFileCheck()
print(json_obj.read_data())
# 二进制数据处理类

Ⅱ 多态

【一】什么是多态

  • 多态指的是一类事物有多种形态

【二】示例

  • 比如动物有多种形态:猫、狗、猪
import abc


# 同一类事物:动物
class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def talk(self):
        pass


# 动物的形态之一:人
class People(Animal):
    def talk(self):
        print('你真帅')


# 动物的形态之二:狗
class Dog(Animal):
    def talk(self):
        print('汪汪汪')


# 动物的形态之三:猪
class Pig(Animal):
    def talk(self):
        print('哼唧哼唧')
  • 文件有多种形态:文本文件,可执行文件
import abc


# 同一类事物:文件
class File(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def click(self):
        pass

# 文件的形态之一:文本文件
class Text(File):  
    def click(self):
        print('open file')

# 文件的形态之二:可执行文件
class ExeFile(File):  
    def click(self):
        print('execute file')

【三】多态性

【1】什么是多态动态绑定(多态性)

  • 多态动态绑定在继承的背景下使用时,有时也称为多态性
  • 多态性是指在不考虑实例类型的情况下使用实例
  • 在面向对象方法中一般是这样表述多态性:
    • 向不同的对象发送同一条消息(!!!obj.func():是调用了obj的方法func,又称为向obj发送了一条消息func)
    • 不同的对象在接收时会产生不同的行为(即方法)。
    • 也就是说,每个对象可以用自己的方式去响应共同的消息。
    • 所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
  • 比如:老师.下课铃响了(),学生.下课铃响了()
    • 老师执行的是下班操作
    • 学生执行的是放学操作
    • 虽然二者消息一样,但是执行的效果不同

【2】多态性的分类

  • 多态性分为静态多态性和动态多态性
(1)静态多态性
  • 如任何类型都可以用运算符 + 进行运算
(2)动态多态性
import abc


# 同一类事物:动物
class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def talk(self):
        pass


# 动物的形态之一:人
class People(Animal):
    def talk(self):
        print('你真帅')


# 动物的形态之二:狗
class Dog(Animal):
    def talk(self):
        print('汪汪汪')


# 动物的形态之三:猪
class Pig(Animal):
    def talk(self):
        print('哼唧哼唧')


peo = People()
dog = Dog()
pig = Pig()

# peo、dog、pig都是动物,只要是动物肯定有talk方法
# 于是我们可以不用考虑它们三者的具体是什么类型,而直接使用
peo.talk()
dog.talk()
pig.talk()


# 更进一步,我们可以定义一个统一的接口来使用
def func(obj):
    obj.talk()

【3】为什么要用多态性(多态性的好处)

  • 增加了程序的灵活性
    • 以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)
  • 增加了程序额可扩展性
    • 通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用
import abc


# 同一类事物:动物
class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def talk(self):
        pass


# 属于动物的另外一种形态:猫
class Cat(Animal):
    def talk(self):
        print('喵喵喵')


# 对于使用者来说,自己的代码根本无需改动
def func(animal):
    animal.talk()


# 实例出一只猫
cat1 = Cat()
# 甚至连调用方式也无需改变,就能调用猫的talk功能
func(cat1)
  • 这样我们新增了一个形态Cat,由Cat类产生的实例cat1,使用者可以在完全不需要修改自己代码的情况下。
  • 使用和人、狗、猪一样的方式调用cat1的talk方法,即func(cat1)

【四】鸭子类型 duck-typing

【1】什么是鸭子类型 duck-typing

  • 鸭子类型是一种编程风格,决定一个对象是否有正确的接口
    • 关注点在于它的方法或属性
    • 而不是它的类型(如果它看起来像鸭子,像鸭子一样嘎嘎叫,那么它一定是鸭子。)。
  • 通过强调接口而不是特定类型,设计良好的代码通过多态提高了灵活性。
    • 鸭子类型无需使用 type() 或 isinstance() 进行检查(注意,鸭子类型可以用抽象基类来补充)
    • 相反,它通常使用 hasattr() 来检查,或是 EAFP 编程。
  • 但其实我们完全可以不依赖于继承,只需要制造出外观和行为相同对象,同样可以实现不考虑对象类型而使用对象,这正是Python崇尚的“鸭子类型”(duck typing):
    • “如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子”。
    • 比起继承的方式,鸭子类型在某种程度上实现了程序的松耦合度
# 【1】在java里面
# 定义接口,并且继承后的接口必须重写
# 【2】在python不一样
# 强制重写父类方法:抽象类

【2】鸭子类型举例

# 二者都像鸭子,因而就可以当鸭子一样去用
class NormalDuck():
    def eat(self):
        print(f"正常鸭子可以吃饭")

    def walk(self):
        print(f"正常鸭子可以走路")


class RockDuck():
    def eat(self):
        print(f"肉鸭子可以吃饭")

    def walk(self):
        print(f"肉鸭子可以走路")

Ⅲ 绑定方法和非绑定方法

【一】绑定方法

  • 绑定给谁,谁来调用就自动将它本身当作第一个参数传入
# 区别在于绑定给谁去使用
class Student(object):
    def __init__(self, name):
        self.name = name

    def talk(self):
        print(f'{self.name} is talking')

# Student.talk()
s = Student('silence')
s.talk()  # silence is talking

【1】绑定到类的方法

  • 用classmethod装饰器装饰的方法。
  • 为类量身定制
  • 类.boud_method(),自动将类当作第一个参数传入
  • (其实对象也可调用,但仍将类当作第一个参数传入)
# 对象和类都可以任意调用的方法
class Student(object):
    def __init__(self, name):
        self.name = name

    # 【1】绑定给对象的方法
    # 对象可以直接调用 obj.talk()
    # 类调用需要传入生成的对象 clss.talk(obj)
    def talk(self):
        print(self)  # <__main__.Student object at 0x0000017EE9CDC640>
        print(f'{self.name} is talking')

    # 【2】绑定给类的方法
    @classmethod
    def read(cls, *args, **kwargs):
        '''

        :return: 调用当前方法的类
        '''
        # print(cls) # <class '__main__.Student'>
        obj = cls(*args, **kwargs)  # Student(*args,**kwargs)
        print(f'{obj.name} is reading')


stu = Student('silence')
# print(Student) # <class '__main__.Student'>
# (1)对象调用绑定给类的方法, 默认将实例化得到当前对象的类自动传入
stu.read('silence')
# (2)类调用绑定给类的方法,类可以直接调用,默认将调用当前方法的类自动传入
Student.read('silence')

【2】绑定到对象的方法

  • 没有被任何装饰器装饰的方法
  • 为对象量身定制
  • 对象.boud_method(),自动将对象当作第一个参数传入
  • (属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说)
# 对象可以任意调用的方法
class Student(object):
    def __init__(self, name):
        self.name = name

    def talk(self):
        print(f'{self.name} is talking')

# (1)对象可以直接调用绑定给对象的方法
s = Student('silence')
s.talk() # 默认将 s 作为 self 自动传入
# (2)类调用绑定给对象的方法,需要主动传入一个生成的对象
Student.talk(s)

【二】非绑定方法

# 既不给对象,又不给类
class Student(object):
    def __init__(self, name):
        self.name = name

    # 【1】绑定给对象的方法
    # 对象可以直接调用 obj.talk()
    # 类调用需要传入生成的对象 clss.talk(obj)
    def talk(self):
        print(self)  # <__main__.Student object at 0x0000017EE9CDC640>
        print(f'{self.name} is talking')

    # 【2】绑定给类的方法
    # 对象调用默认将实例化得到当前对象的类自动传入
    # 对象调用默认将当前类传入
    @classmethod
    def read(cls, *args, **kwargs):
        '''

        :return: 调用当前方法的类
        '''
        # print(cls) # <class '__main__.Student'>
        obj = cls(*args, **kwargs)  # Student(*args,**kwargs)
        print(f'{obj.name} is reading')

    # 【3】非绑定方法
    # 对象调用非绑定方法,直接调用
    # 类调用非绑定方法,直接调用
    @staticmethod
    def write():
        print("is writing")


stu = Student('silence')
# print(Student) # <class '__main__.Student'>
# (1)对象调用非绑定方法, 不用传任何参数,和普通函数一样
stu.write()
# (2)类调用非绑定方法, 不用传任何参数,和普通函数一样
Student.write()

【三】总结

# 【1】绑定方法
# (1)绑定给对象的方法
# 我们正常在函数内部定义的方法
# 特征就是自动补全self
# 对象调用直接调用(默认将当前的对象作为self默认参数传入) obj.func()
# 类调用需要传递额外的参数,额外的参数就是实例化得到的对象  class.func(obj)
'''
def func(self):
'''

# (2)绑定给类的方法
# 需要在类内部用 @classmethod 装饰的函数
# 特征就是自动补全cls 并且有 @classmethod 装饰
# 对象调用直接调用(默认将当前的对象的类作为cls默认参数传入) obj.func()
# 类调用直接调用,默认将当前类作为 cls 自动传入  class.func()
'''
@classmethod
def index(cls):
'''

# 【2】非绑定方法
# 需要在类内部用 @staticmethod 装饰的函数
# 特征不会自动补全任何参数
# 对象调用直接调用
# 类调用直接调用
'''
@staticmethod
def foo():
'''

Ⅳ 反射

【一】反射

【1】什么是反射

  • 反射是一种程序可以访问、检测和修改其本身状态或行为的能力。
  • 在 Python 中,反射主要指通过字符串的形式操作对象的属性。

【2】Python中的反射

  • 通过字符串的形式操作对象相关的属性。
  • python中的一切事物都是对象(都可以使用反射)

【二】反射方法

【1】反射方法介绍

  • getattr(object, key)
    • 获取对象的属性值,如果属性不存在,可提供默认值。
  • hasattr(object, key)
    • 判断对象是否具有指定属性
  • setattr(object, key, value)
    • 设置对象的属性值
  • delattr(object, key)
    • 删除对象的属性

【三】具体使用

【1】引入

class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def run(self):
        print("人能跑 ...")


person = Person('silence', 18)

# 直接通过对象获取属性对应的值
print(person.name)
# silence

# 可以查看类的属性,不能查看方法
print(person.__dict__)
# {'name': 'silence', 'age': 18}

# 根据字典取值:拿到字典中对应的值
print(person.__dict__['name'])
# silence

# 查看对象中的所有方法
print(person.__dir__())
# ['name', 'age', '__module__', '__init__', 'run', '__dict__', '__weakref__', '__doc__', '__new__', '__repr__', '__hash__', '__str__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']

# 查看 对象中 的所有方法
print(dir(person))
# ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'run']

【2】getattr(object, key)

  • getattr(object, key)
    • 获取对象的属性值,如果属性不存在,可提供默认值。
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def run(self):
        print("人能跑 ...")

# 实例化得到一个对象
person = Person('silence', 18)

# (1)数据属性
# 在对象中映射数据属性的时候,如果对象中存在当前属性值则直接将属性值拿出来
res_name = getattr(person, 'name')
print(f"res_name :>>>> {res_name}")
# res_name :>>>> silence

res_age = getattr(person, 'age')
print(f"res_age :>>>> {res_age}")
# res_age :>>>> 18

# (2)函数属性
# 在对象中映射函数属性的时候,如果对象中存在当前属性名对应的数据属性
# 则直接获取当前函数属性的内存地址,可以直接调用当前函数
res_run = getattr(person, 'run')
print(f"res_run :>>>> {res_run}")
# res_run :>>>> <bound method Person.run of <__main__.Person object at 0x00000262973CE800>>

# (3)不存在
# 在对象中映射属性的时候,如果对象中不存在当前属性名对应的属性,会直接报错
res_smile = getattr(person, 'smile')
print(f"res_smile :>>>> {res_smile}")
 r"""
 Traceback (most recent call last):
  File "E:\pythonprojects\test.py", line 25, in <module>
    res_smile = getattr(person, 'smile')
AttributeError: 'Person' object has no attribute 'smile'
"""

【3】hasattr(object, key)

  • hasattr(object, key)
    • 判断对象是否具有指定属性
# 常用
# hasattr  判断对象是否存在某属性或方法
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def run(self):
        print("人能跑 ...")


person = Person('silence', 18)

# (1)数据属性
# 在对象中映射数据属性的时候,如果对象中存在当前属性值则返回True
res_name = hasattr(person, 'name')
print(f"res_name :>>>> {res_name}")
# res_name :>>>> True

res_age = hasattr(person, 'age')
print(f"res_age :>>>> {res_age}")
# res_age :>>>> True

# (2)函数属性
# 在对象中映射函数属性的时候,如果对象中存在当前属性名对应的数据属性,则返回True
res_run = hasattr(person, 'run')
print(f"res_run :>>>> {res_run}")
# res_run :>>>> True

# (3)不存在
# 在对象中映射属性的时候,如果对象中不存在当前属性名对应的属性,则返回False
res_smile = hasattr(person, 'smile')
print(f"res_smile :>>>> {res_smile}")
# res_smile :>>>> False

【4】setattr(object, key, value)

  • setattr(object, key, value)
    • 设置对象的属性值
class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def tell(self):
        print(f'{self.name} is {self.age} years old')

    @classmethod
    def talk(cls):
        print(cls.__name__)

    @staticmethod
    def swim():
        print(f'I am a swimming')


# 实例化得到一个对象
people = Person(name='silence', age=18)


# (1)数据属性
# 向对象中设置属性名和属性值,如果对象中存在当前属性则直接替换,否则新增
result = setattr(people, 'name', 'happy')
result = setattr(people, 'gender', 'male')
print(result)  # None
print(people.name)  # happy
print(people.gender)  # male
# (2)函数属性
def read():
    print(f"这是外部的 read ")
# 在对象中映射函数属性的时候,如果对象中存在当前属性名对应的数据属性,则返回True
result = setattr(people, 'read', read)
print(result)  # None
print(hasattr(people, 'read'))  # True
print(getattr(people, 'read'))  # <function read at 0x0000018459EF37F0>
getattr(people, 'read')()  # 这是外部的 read
print(people.tell)  # <bound method Person.tell of <__main__.Person object at 0x000001845A107D60>>
print(people.talk)  # <bound method Person.talk of <class '__main__.Person'>>
print(people.swim)  # <function Person.swim at 0x000001845A1120E0>

【5】delattr(object, key)

  • delattr(object, key)
    • 删除对象的属性
class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def tell(self):
        print(f'{self.name} is {self.age} years old')

    @classmethod
    def talk(cls):
        print(cls.__name__)

    @staticmethod
    def swim():
        print(f'I am a swimming')


# 实例化得到一个对象
people = Person(name='silence', age=18)


# (1)数据属性
# 在对象中删除数据属性的时候,如果对象中存在当前属性值则直接删除
print(hasattr(people,'name')) # True
result = delattr(people,'name')
print(result) # None
print(hasattr(people,'name')) # False
# (2)函数属性
# 在对象中删除函数属性的时候,要根据参数是对象还是类来做区分
print(hasattr(people, 'tell'))  # True
# 如果参数是当前对象,则无法删除函数属性
result = delattr(people, 'tell')  # 绑定给对象的方法无法删除
result = delattr(people, 'talk')
result = delattr(people, 'swim')
# 如果参数是当前类,则可以删除函数属性
print(hasattr(Person, 'tell'))
result = delattr(Person, 'tell') # 绑定给类的方法可以删除
result = delattr(Person, 'talk')
result = delattr(Person, 'swim')
print(result)  # None
print(hasattr(Person, 'swim'))
# (3)不存在
# 在对象中删除属性的时候,如果对象中不存在当前属性名对应的属性,则直接报错
result = delattr(people,'gender')
print(result) #AttributeError: gender

Ⅴ 魔法方法(内置方法)

所谓的“魔法方法”(Magic Methods 或 Special Methods)是那些以双下划线(__)开始和结束的方法。这些方法是Python语言定义的一些特殊方法,它们允许类的实例对象具有一些特殊的行为。当某些特定的事件发生时,Python解释器会自动调用这些魔法方法。

【引入】

  • Python的Class机制内置了很多特殊的方法来帮助使用者高度定制自己的类
  • 这些内置方法都是以双下划线开头和结尾的,会在满足某种条件时自动触发
__init__	:初始化类时触发
__del__		:删除类时触发
__new__		:构造类时触发
__str__		:str函数或者print函数触发
__repr__	:repr或者交互式解释器触发
__doc__		:打印类内的注释内容
__enter__	:打开文档触发
__exit__	:关闭文档触发
__getattr__ : 访问不存在的属性时调用
__setattr__ :设置实例对象的一个新的属性时调用
__delattr__ :删除一个实例对象的属性时调用
__setitem__	:列表添加值
__getitem__ :将对象当作list使用 
__delitem__	:列表删除值
__call__	:对象后面加括号,触发执行
__iter__	:迭代器
# 在类内部达到指定条件会自动触发的方法
# __init__ : 实例化类得到对象的时候会自动触发
class Student(object):
    # 【1】__init__ : 实例化类得到对象的时候会自动触发
    def __init__(self, name):
        print(f"实例化类的时候触发")
        self.name = name
        # self.fp = open('./data.text', 'r', encoding='utf-8')
        # print(f"当前文件对象已打开 :>>>> {self.fp.closed}")
        print('-------')

    # 【2】__del__ : 当对象/对象关闭销毁的时候自动触发
    # 场景:打开文件 open --> close
    def __del__(self):
        # self.fp.close()
        # print(f"当前文件对象已关闭 :>>>> {self.fp.closed}")
        print(f"当前在销毁的时候触发")
        print('-------')

    # 【3】__str__ : 在打印当前对象的时候可以定制打印当前对象的显示内容
    # 必须且只能返回字符串类型的数据
    def __str__(self):
        print(f'打印当前对象的时候会触发')
        print('-------')
        return self.name

    # 【4】__repr__ : 交互解释器会触发
    def __repr__(self):
        print(f"与解释器交互的时候会触发")
        print('-------')
        return self.name

    # 【5】__doc__ : 打印类里面的注释内容的时候会触发
    # 对象.__doc__
    # 类.__doc__
    __doc__ = "这是一个学生类"

    # 【6】__enter__  : 打开文档的时候会触发 with 语句触发
    def __enter__(self):
        print(f"打开文档的时候会触发")
        print('-------')
        return self

    # 【7】__exit__ : 关闭文档的时候会触发 with 语句触发
    def __exit__(self, exc_type, exc_val, exc_tb):
        print(f"关闭文档的时候会触发")
        print('-------')
        ...

    # 【8】__getattr__ : 获取当前对象的不存在的属性的时候触发
    # __getattribute__ : 值不存在的时候会触发异常
    def __getattr__(self, item):
        print(f'当前对象属性不存在的时候会触发')
        print(item)  # 就是不存在的属性的变量名
        # 父类里面有一个 __getattribute__ 能主动抛出值不存在的异常
        super().__getattribute__(item)
        print('-------')

    # 【9】__setattr__ : 设置对象的属性值的时候会触发(包括 __init__ 初始化属性)
    def __setattr__(self, key, value):
        print(f'设置当前对象属性的时候会触发 对象.key=value')
        print(f"key :>>>>> {key}")
        print(f"value :>>>>> {value}")
        print('-------')

    # 【10】__setattr__ : 删除对象的属性值的时候会触发
    def __delattr__(self, item):
        print(f'当前在删除属性的时候会触发')
        print(item)
        print('-------')

    # 【11】__setitem__ : 对象[key]=value 设置值的时候会触发
    def __setitem__(self, key, value):
        print(f'设置属性的时候会触发 设置方式为 对象[key]=value')
        print(key, value)
        print('-------')
        # 用自己的名称空间字典放
        self.__dict__[key] = value

    # 【12】__getitem__ : 获取属性的时候会触发 设置方式为 对象[key]
    def __getitem__(self, item):
        print(f'获取属性的时候会触发 设置方式为 对象[key]')
        print('-------')
        return self.__dict__[item]

    # 【13】__delitem__ : 删除属性的时候会触发 删除方式为 del 对象[key]
    def __delitem__(self, key):
        print(f'删除属性的时候会触发 删除方式为 del 对象[key]')
        print(key)
        print('-------')


# s = Student(name='silence')
# print(s)  # <__main__.Student object at 0x0000023FE2084E50>
# print(repr(s))  # 触发 __repr__
# print(s.__doc__)
# print(Student.__doc__)

# with Student('hope') as fp:
#     print(fp,type(fp))

# s = Student(name='dream')
# print(s.name)
# print(s.age)

# s.age = 18
# del s.age
# print(s.age)

# s['gender'] = 'male'
# print(s['gender'])
#
# del s['gender']

【一】init(),del()new()

  • Python的Class机制内置了很多特殊的方法来帮助使用者高度定制自己的类
  • 这些内置方法都是以双下划线开头和结尾的,会在满足某种条件时自动触发
  • 这个方法是一个类的构造函数,与之对应的__del__()是析构函数,通过此方法我们可以定义一个对象的初始操作。
  • 但实际上,新式类的__new__()才是真正的初始化函数。
class A(object):
    def __init__(self):
        print('__init__')

    def __new__(cls, *args, **kwargs):
        print('__new__')
        # cls表示一个类,一个当前要被实例化的类,参数由py解释器自动提供
        return super().__new__(cls, *args, **kwargs)

    def __del__(self):
        print('__del__')


a = A()
print('do something')
# __new__
# __init__
# do something
# __del__

# 实际上,
# __new__()负责创建一个对象,
# __init__负责定制化该对象,即是在对象创建好之后初始化变量

既然知道了__new__()方法,我们是不是可以考虑一下,如何应用它呢?最常见的就是单例模式了,下面给出实现实例

class Singleton(object):
    _instance = None

    def __new__(cls, *args, **kwargs):
        """
        注意这实际上是一个类方法, cls 表示当前类
        :param args:
        :param kwargs:
        :return:
        """
        if cls._instance is None:
            cls._instance = super().__new__(cls, *args, **kwargs)

        return cls._instance


s1 = Singleton()
s2 = Singleton()
if s1 is s2:
    print('yeah')

【二】str,repr

【1】str

  • __str__方法会在对象被打印时自动触发
    • print功能打印的就是它的返回值
    • 我们通常基于方法来定制对象的打印信息
    • 该方法必须返回字符串类型
class People:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        # 返回类型必须是字符串
        return f'<Name:{self.name} Age:{self.age}>'


person = People('silence', 18)
print(person)  # 触发p.__str__(),拿到返回值后进行打印
# <Name:dream Age:18>

【2】repr

  • repr或者交互式解释器触发
class School:
    def __init__(self, name, addr, type):
        self.name = name
        self.addr = addr
        self.type = type

    def __repr__(self):
        return 'School(%s,%s)' % (self.name, self.addr)

    def __str__(self):
        return '(%s,%s)' % (self.name, self.addr)


s1 = School('dreamCity', '北京', '私立')
print('from repr: ', repr(s1))
print('from str: ', str(s1))
print(s1)

【3】小结

  • str函数或者print函数---> obj.str()
  • repr或者交互式解释器---> obj.repr()
  • 如果__str__没有被定义,那么就会使用__repr__来代替输出
  • 注意:这俩方法的返回值必须是字符串,否则抛出异常

【三】__del__方法

  • __del__会在对象被删除时自动触发。
  • 由于Python自带的垃圾回收机制会自动清理Python程序的资源,所以当一个对象只占用应用程序级资源时,完全没必要为对象定制__del__方法
  • 但在产生一个对象的同时涉及到申请系统资源(比如系统打开的文件、网络连接等)的情况下
    • 关于系统资源的回收,Python的垃圾回收机制便派不上用场了
    • 需要我们为对象定制该方法,用来在对象被删除时自动触发回收系统资源的操作
class MySQL:
    def __init__(self, ip, port):
        # 伪代码,发起网络连接,需要占用系统资源
        self.conn = connect(ip, port)

    def __del__(self):
        # 关闭网络连接,回收系统资源
        self.conn.close()

    
# 在对象obj被删除时,自动触发obj.__del__()
obj = MySQL('127.0.0.1', 3306)
class Person():
    def __init__(self, name, age):
        print(f'这是 :>>>> {name} , age :>>>> {age}')

    def __del__(self):
        print(f"我离开你了")


person = Person("silence", 18)
print("这是断层")

【四】doc

class Foo:
    '我是描述信息'
    pass


print(Foo.__doc__)
class Foo:
    '我是描述信息'
    pass

class Bar(Foo):
    pass

# 该属性无法继承给子类
print(Bar.__doc__)

【五】enter__和__exit

# 我们知道在操作文件对象的时候可以这么写

with open('a.txt') as f:
  '代码块'
  • 上述叫做上下文管理协议,即with语句
  • 为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法
class Open:
    def __init__(self, name):
        self.name = name

    def __enter__(self):
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
        # return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行我啊')


with Open('a.txt') as f:
    print('=====>执行代码块')
    # print(f,f.name)
  • exit()中的三个参数分别代表
    • 异常类型
    • 异常值
    • 追溯信息
  • with语句中代码块出现异常,则with后的代码都无法执行
class Open:
    def __init__(self, name):
        self.name = name

    def __enter__(self):
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行我啊')
        print(exc_type)
        print(exc_val)
        print(exc_tb)


with Open('a.txt') as f:
    print('=====>执行代码块')
    raise AttributeError('***着火啦,救火啊***')
print('0' * 100)  # ------------------------------->不会执行
  • 如果__exit__()返回值为True,那么异常会被清空,就好像啥都没发生一样
    • with后的语句正常执行
class Open:
    def __init__(self, name):
        self.name = name

    def __enter__(self):
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行我啊')
        print(exc_type)
        print(exc_val)
        print(exc_tb)
        return True


with Open('a.txt') as f:
    print('=====>执行代码块')
    raise AttributeError('***着火啦,救火啊***')
print('0' * 100)  # ------------------------------->会执行

# 出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
# =====>执行代码块
# with中代码块执行完毕时执行我啊
# <class 'AttributeError'>
# ***着火啦,救火啊***
# <traceback object at 0x000002591C0AC140>
# 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

【六】setattr,delattr,getattr

【1】方法介绍

  • getattr(self, name): 访问不存在的属性时调用
  • setattr(self, name, value):设置实例对象的一个新的属性时调用
  • delattr(self, name):删除一个实例对象的属性时调用
class Foo:
    x = 1

    def __init__(self, y):
        self.y = y

    def __getattr__(self, item):
        print(f'----> from getattr:你找的属性不存在 :>>>> {item}')

    def __setattr__(self, key, value):
        print(f'----> from setattr  key : {key}  value : {value}')
        # self.key=value #这就无限递归了,你好好想想
        # self.__dict__[key]=value #应该使用它

    def __delattr__(self, item):
        print(f'----> from delattr :>>>> {item}')
        # del self.item #无限递归了
        self.__dict__.pop(item)


# __setattr__添加/修改属性会触发它的执行
f1 = Foo(10)

# 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
print(f1.__dict__)
f1.z = 3
print(f1.__dict__)

# __delattr__删除属性的时候会触发
f1.__dict__['a'] = 3  # 我们可以直接修改属性字典,来完成添加/修改属性的操作
del f1.a
print(f1.__dict__)

# __getattr__只有在使用点调用属性且属性不存在的时候才会触发
f1.xxxxxx
  • 对象属性查找顺序
    • 首先访问__getattribute__()魔法方法(隐含默认调用,无论何种情况,均会调用此方法
    • 去实例对象t中查找是否具备该属性: t.__dict__中查找,每个类和实例对象都有一个__dict__的属性
    • 若在t.__dict__中找不到对应的属性, 则去该实例的类中寻找,即 t.class.dict
    • 若在实例的类中也招不到该属性,则去父类中寻找,即t.class.bases.__dict__中寻找
    • 若以上均无法找到,则会调用__getattr__方法,执行内部的命令(若未重载 __getattr__方法,则直接报错:AttributeError)

【七】setitem,getitem,delitem

  • getitem()将对象当作list使用
  • 如obj = ACLASS(), obj_first=obj[0]
class Foo:
    def __init__(self, name):
        self.name = name

    def __getitem__(self, item):
        print(self.__dict__[item])

    def __setitem__(self, key, value):
        self.__dict__[key] = value

    def __delitem__(self, key):
        print('del obj[key]时,我执行')
        self.__dict__.pop(key)

    def __delattr__(self, item):
        print('del obj.key时,我执行')
        self.__dict__.pop(item)


f1 = Foo('sb')
f1['age'] = 18
f1['age1'] = 19
del f1.age1
del f1['age']
f1['name'] = 'alex'
print(f1.__dict__)

【八】call

  • 对象后面加括号,触发执行。
  • 注:构造方法的执行是由创建对象触发的
    • 即:对象 = 类名() ;
  • 而对于 call 方法的执行是由对象后加括号触发的
    • 即:对象() 或者 类()()
class Foo:

    def __init__(self):
        pass

    def __call__(self, *args, **kwargs):
        print('__call__')


obj = Foo()  # 执行 __init__
obj()  # 执行 __call__

【九】iter()

  • 为for … in提供接口,返回一个迭代对象,并调用对象next()方法,直到StopIteration
class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1  # 初始化两个计数器a,b

    def __iter__(self):
        return self  # 实例本身就是迭代对象,故返回自己

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b  # 计算下一个值
        if self.a > 10:  # 退出循环的条件
            raise StopIteration()
        return self.a  # 返回下一个值


for i in Fib():
    print(i)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值