首页 > 编程语言 >python面向对象之类的内置方法

python面向对象之类的内置方法

时间:2024-01-14 18:33:45浏览次数:34  
标签:__ 内置 python self 面向对象 pass print class def

【引入】

  • Python的Class机制内置了很多特殊的方法来帮助使用者高度定制自己的类
  • 这些内置方法都是以双下划线开头和结尾的,会在满足某种条件时自动触发
__init__	:初始化类时触发
__del__		:删除类时触发
__new__		:构造类时触发
__str__		:str函数或者print函数触发
__repr__	:repr或者交互式解释器触发
__doc__		:打印类内的注释内容
__enter__	:打开文档触发
__exit__	:关闭文档触发
__getatrribute__:属性访问截断器,属性查找中优先级最高的
__getattr__ : 访问不存在的属性时调用
__setattr__ :设置实例对象的一个新的属性时调用
__delattr__ :删除一个实例对象的属性时调用
__setitem__	:列表添加值
__getitem__ :将对象当作list使用 
__delitem__	:列表删除值
__call__	:对象后面加括号,触发执行
__iter__	:迭代器

【一】__init__()__new__()

__new__方法是真正的类构造方法,用于产生实例化对象(空属性)。重写__new__方法可以控制对象的产生过程。
__init__方法是初始化方法,负责对实例化对象进行属性值初始化,此方法必须返回None,__new__方法必须返回一个对象。重写__init__方法可以控制对象的初始化过程。

# 使用new来处理单例模式

class Student:
    __instance = None

    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            cls.__instance = object.__new__(cls)
        return cls.__instance

    def sleep(self):
        print('sleeping...')

stu1 = Student()
stu2 = Student()

print(id(stu1), id(stu2))  # 两者输出相同
print(stu1 is stu2)  # True

​ 个人感觉,__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('xiao', 18)
print(person)  # 触发p.__str__(),拿到返回值后进行打印
# <Name:xiao 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('City', '上海', '公办')
print('from repr: ', repr(s1))
print('from str: ', str(s1))
print(s1)

【3】小结

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

【三】__del__方法

__del__用于当对象的引用计数为0时自动调用。
__del__一般出现在两个地方:

​ 1. 手工使用del减少对象引用计数至0,被垃圾回收处理时调用。

2. 程序结束时调用。

__del__一般用于需要声明在对象被删除前需要处理的资源回收操作

# 手工调用del 可以将对象引用计数减一,如果减到0,将会触发垃圾回收

class Student:

    def __del__(self):
        print('调用对象的del方法,此方法将会回收此对象内存地址')

stu = Student()  # 调用对象的__del__方法回收此对象内存地址

del stu

print('下面还有程序其他代码')
class Student:

    def __del__(self):
        print('调用对象的del方法,此方法将会回收此对象内存地址')

stu = Student()  # 程序直接结束,也会调用对象的__del__方法回收地址

【四】isinstance(obj,cls)issubclass(sub,super)

  • isinstance(obj,cls)检查是否obj是否是类cls 的对象
class Bar():
    pass


class Foo(object):
    pass


foo = Foo()
bar = Bar()

# isinstance(obj,cls)检查是否obj是否是类cls的对象
res_Foo = isinstance(foo, Foo)
res_Bar = isinstance(foo, Bar)
print(res_Foo)
print(res_Bar)
  • issubclass(sub, super)检查sub类是否是 super 类的派生类
class Foo(object):
    pass


class Bar(Foo):
    pass


res = issubclass(Bar, Foo)
print(res)

【五】__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____getatrribute__

【1】方法介绍

  1. setattr方法:当使用obj.x = y的时候触发对象的setattr方法;

  2. delattr方法:当del obj.x的时候触发对象的delattr方法;

  3. getattr方法:当尝试访问对象的一个不存在的属性时 obj.noexist 会触发getattr方法;

getattr方法是属性查找中优先级最低的。可以重写这3个方法来控制对象属性的访问、设置和删除。

特别注意:如果定义了getattr,而没有任何代码(即只有pass),则所有不存在的属性值都是None而不会报错,可以使用super().getattr()方法来处理

class Student:
    def __getattr__(self, item):
        print('访问一个不存在的属性时候触发')
        return '不存在'

    def __setattr__(self, key, value):
        print('设置一个属性值的时候触发')
        # self.key = value  # 这样会无限循环
        self.__dict__[key] = value

    def __delattr__(self, item):
        print('删除一个属性的时候触发')
        if self.__dict__.get(item, None):
            del self.__dict__[item]

stu = Student()
stu.name = 'xiao'  # 设置一个属性值的时候触发
print(stu.noexit)  # 访问一个不存在的属性时候触发 , 返回'不存在'
del stu.name  # 删除一个属性的时候触发

__getatrribute__

这是一个属性访问截断器,即,在你访问属性时,这个方法会把你的访问行为截断,并优先执行此方法中的代码,此方法应该是属性查找顺序中优先级最高的。
属性查找顺序:
实例的getattribute–>实例对象字典–>实例所在类字典–>实例所在类的父类(MRO顺序)字典–>实例所在类的getattr–>报错

class People:
    a = 200

class Student(People):
    a = 100

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

    def __getattr__(self, item):
        print('没有找到:', item)

    def __getattribute__(self, item):
        print('属性访问截断器')
        if item == 'a':
            return 1
        return super().__getattribute__(item)

stu = Student(1)
print(stu.a)  # 1

对象属性查找顺序

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

【2】二次加工标准类型(包装)

(1)包装

  • 包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)
# 继承list所有的属性,也可以派生出自己新的,比如append和mid
class List(list):
    def append(self, p_object):
        ' 派生自己的append:加上类型检查'
        if not isinstance(p_object, int):
            raise TypeError('must be int')
        super().append(p_object)

    @property
    def mid(self):
        '新增自己的属性'
        index = len(self) // 2
        return self[index]


l = List([1, 2, 3, 4])
print(l)
l.append(5)
print(l)
# l.append('1111111') #报错,必须为int类型

print(l.mid)

# 其余的方法都继承list的
l.insert(0, -123)
print(l)

l.clear()
print(l)

(2)授权

  • 授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。

  • 授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。

  • 实现授权的关键点就是覆盖__getattr__方法

import time


class FileHandle:
    def __init__(self, filename, mode='r', encoding='utf-8'):
        # 定义一个文件句柄 ---- 文件打开 并 将文件句柄给 self.file
        self.file = open(filename, mode, encoding=encoding)

    def write(self, line):
        t = time.strftime('%Y-%m-%d %T')
        self.file.write('%s %s' % (t, line))

    def __getattr__(self, item):
        # 返回 映射方法 ---- 映射自己self.file 是否存在 read 方法
        # print(item) # read
        return getattr(self.file, item)


# 实例化类得到对象
f1 = FileHandle('b.txt', 'w+')
# 写入文本内容
f1.write('你好啊')
# 移动光标
f1.seek(0)
# 触发 __getattr__(self, item) 方法 , self 是 f1 对象 ,item 是 read 方法
print(f1.read())
# 关闭 对象
f1.close()
# 自定义文件处理器
class FileHandle:
    def __init__(self, filename, mode='r', encoding='utf-8'):
        if 'b' in mode:
            self.file = open(filename, mode)
        else:
            self.file = open(filename, mode, encoding=encoding)
        self.filename = filename
        self.mode = mode
        self.encoding = encoding

    def write(self, line):
        if 'b' in self.mode:
            if not isinstance(line, bytes):
                raise TypeError('must be bytes')
        self.file.write(line)

    def __getattr__(self, item):
        return getattr(self.file, item)

    def __str__(self):
        if 'b' in self.mode:
            res = "<_io.BufferedReader name='%s'>" % self.filename
        else:
            res = "<_io.TextIOWrapper name='%s' mode='%s' encoding='%s'>" % (self.filename, self.mode, self.encoding)
        return res


f1 = FileHandle('b.txt', 'wb')
# f1.write('你好啊啊啊啊啊') 
# #自定制的write,不用在进行encode转成二进制去写了,简单,大气
f1.write('你好啊'.encode('utf-8'))
print(f1)
f1.close()

【八】__setitem__,__getitem__,__delitem__

重写此系列方法可以模拟对象成列表或者是字典,即可以使用key-value的类型。

class StudentManager:
    li = []
    dic = {}

    def add(self, obj):
        self.li.append(obj)
        self.dic[obj.name] = obj

    def __getitem__(self, item):
        if isinstance(item, int):
            # 通过下标得到对象
            return self.li[item]
        elif isinstance(item, slice):
            # 通过切片得到一串对象
            start = item.start
            stop = item.stop
            return [student for student in self.li[start:stop]]
        elif isinstance(item, str):
            # 通过名字得到对象
            return self.dic.get(item, None)
        else:
            # 给定的key类型错误
            raise TypeError('你输入的key类型错误!')

class Student:
    manager = StudentManager()

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

        self.manager.add(self)

    def __str__(self):
        return f'学生: {self.name}'

    __repr__ = __str__


stu1 = Student('小明')
stu2 = Student('大白')
stu3 = Student('小红')
stu4 = Student('胖虎')

# 当做列表使用
print(Student.manager[0])  # 学生: 小明
print(Student.manager[-1])  # 学生: 胖虎
print(Student.manager[1:3])  # [学生: 大白, 学生: 小红]

# 当做字典使用
print(Student.manager['胖虎'])  # 学生: 胖虎

【九】__call__

  • 对象后面加括号,触发执行。

  • 注:构造方法的执行是由创建对象触发的

    • 即:对象 = 类名() ;
  • 而对于 __call__ 方法的执行是由对象后加括号触发的

    • 即:对象() 或者 类()()
class Foo:

    def __init__(self):
        pass

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


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

【十】__gt__(),__lt__(),__eq__(),__ne__(),__ge__()

  • 定义对象比较方法,为关系运算符>,>=,<,==等提供接口
class SavingsAccount(object):
    def __init__(self, name, pin, balance=0.0):
        self._name = name
        self._pin = pin
        self._balance = balance

    def __lt__(self, other):
        return self._balance < other._balance


s1 = SavingsAccount("dream", "1000", 0)
s2 = SavingsAccount("hope", "1001", 30)
print(s1 < s2)
  • 其实,类似的数值运算的魔法方法非常非常之多,好吧,我真的用了两个,现在是三个非常来形容,以下列出部分
class Point(object):
    def __init__(self):
        self.x = -1
        self.y = 5

    # 正负,取反、绝对值
    def __pos__(self):
        pass

    def __neg__(self):
        pass

    def __invert__(self):
        pass

    def __abs__(self):
        pass

    # 加减乘除、取余,指数
    def __add__(self, other):
        pass

    def __sub__(self, other):
        pass

    def __divmod__(self, other):
        pass

    def __mul__(self, other):
        pass

    def __mod__(self, other):
        pass

    def __pow__(self, power, modulo=None):
        pass

    # 逻辑运算符
    def __and__(self, other):
        pass

    def __or__(self, other):
        pass

    def __xor__(self, other):
        pass

    # 位移
    def __lshift__(self, other):
        pass

    def __rshift__(self, other):
        pass

    # 赋值语句
    def __iadd__(self, other):
        pass

    def __imul__(self, other):
        pass

    def __isub__(self, other):
        pass

    def __idiv__(self, other):
        pass

    def __imod__(self, other):
        pass

    def __ipow__(self, other):
        pass

    def __ilshift__(self, other):
        pass

    def __irshift__(self, other):
        pass

【十一】__iter__()__next__

​ 这2个方法用于将一个对象模拟成序列。内置类型如列表、元组都可以被迭代,文件对象也可以被迭代获取每一行内容。重写这两个方法就可以实现自定义的迭代对象。

# 定义一个指定范围的自然数类,并可以提供迭代

class Num:
    def __init__(self, max_num):
        self.max_num = max_num
        self.count = 0
        
    def __iter__(self):
        return self

    def __next__(self):
        if self.count < self.max_num:
            self.count += 1
            return self.count
        else:
            raise StopIteration('已经到达临界')
        
num = Num(10)
for i in num:
    print(i)  # 循环打印1---10

【十二】描述符(__get__(),__set__(),__delete__())

【1】什么是描述符

  • 描述符本质就是一个新式类,在这个新式类中
    • 至少实现了__get__(),__set__(),__delete__()中的一个
    • 这也被称为描述符协议
  • __get__():调用一个属性时,触发
  • __set__():为一个属性赋值时,触发
  • __delete__():采用del删除属性时,触发
class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符
    def __get__(self, instance, owner):
        pass
    def __set__(self, instance, value):
        pass
    def __delete__(self, instance):
        pass

【2】描述符的作用

  • 描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)

【3】描述符分两种

  • 数据描述符:至少实现了__get__()__set__()
class Foo:
    def __set__(self, instance, value):
        print('set')

    def __get__(self, instance, owner):
        print('get')

标签:__,内置,python,self,面向对象,pass,print,class,def
From: https://www.cnblogs.com/xiao01/p/17964012

相关文章

  • 面向对象编程中级
    IDE编程常用快捷键删除当前行,默认是ctrl+Y自己配置ctrl+d复制当前行,自己配置ctrl+alt+向下光标补全代码alt+/添加注释和取消注释ctrl+/【第一次是添加注释,第二次是取消注释】导入该行需要的类先配置autoimport,然后使用alt+enter即可快速格式化代码ctrl+alt+L快速......
  • 面向对象编程基础
    类和对象的区别和联系类是抽象的,概念的,代表一类事物,比如人类,猫类..,即它是数据类型.对象是具体的,实际的,代表一个具体事物,即是实例.类是对象的模板,对象是类的一个个体,对应一个实例属性,成员变量,字段成员变量=属性=field(字段)(即成员变量是用来表示属性的,统一叫属......
  • python client of influxdb v2
    pythonclienthttps://docs.influxdata.com/influxdb/v2/api-guide/tutorials/python/#authenticate-with-an-influxdb-api-tokenFollowthisstep-by-steptutorialtobuildanInternet-of-Things(IoT)applicationwithInfluxDataclientlibrariesandyourfavorit......
  • Python面向对象之反射
    反射【一】什么是反射反射是一种程序可以访问、检测和修改其本身状态或行为的能力。在Python中,反射主要指通过字符串的形式操作对象的属性。【二】Python中的反射通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)【三】反射方法class......
  • different python version + venv
    ubuntu系统上安装不同python版本https://www.bandwagonhost.net/7309.html比如安装Python3.7:sudoaptinstallpython3.7或者安装Python3.6:sudoaptinstallpython3.6安装之后,我们就可以使用Python对应版本了,比如看一下Python3.7的具体版本:python3.7-V构造应......
  • python二分法查找
    比如要在列表arr中查找xdeff(arr,x):left=0right=len(arr)whileleft<right:mid=(left+right)//2ifmid<x:left=midelifmid>x:right=midelse:returnmid......
  • [Python急救站]学生管理系统链接数据库
    相信很多人在初学Python的时候,经常最后作业就是完成一个学生管理系统,但是我们来做一个完美的学生管理系统,并且将数据储存到数据库里。我们先看看我们的数据库怎么设置。首先呢,我选择用的是SQLServer然后,我们的数据库名称为学生管理系统 接着,新建一张表,我设置表的名称为学生......
  • Python3环境安装
    Ubuntu下自带Python。python3-Vpython3--versionPython3.10.12sudoapt-getinstallpython3-pippip -Vpip3--versionpip22.0.2from/usr/lib/python3/dist-packages/pip(python3.10)sudoaptinstallpython3.10-venvpython3-mvenvmyenvsourcemyenv/bin/activ......
  • Python Flask Class类默认方法(函数)
    前言全局说明Class类默认方法(函数)一、安装flask模块二、引用模块三、启动服务模块安装、引用模块、启动Web服务方法,参考下面链接文章:https://www.cnblogs.com/wutou/p/17963563四、Class类默认方法(函数)默认方法(函数)说明备注init类被调用后,自动执行......
  • Python Flask 模块安装、引用模块、启动Web服务方法
    前言全局说明模块安装、引用模块、启动服务一、安装flask模块官方源:pip3installflask==2.3.2国内源:pip3installflask==2.3.2-ihttp://pypi.douban.com/simple/--trusted-hostpypi.douban.com以上二选一,哪个安装快用哪个flask安装时间2023-11更多国内源:......