面向对象—03魔术方法

魔术方法

在Python中,所有用" __ " 包起来的方法,都称为“魔术方法”(eg: __ len__, __ init__)。
魔术方法一般是为了让显示器调用的,自己并不需要调用它们。

一、特殊属性

魔术方法名称功能
__ name __类,函数,方法等的名称
__ module __类定义所在的模块
__ class __对象或者类所属的类
__ bases __当前类的父类
__ doc __类,函数的帮助文档,没有定义为None
__ mro __方法解析顺序
__ dict __类或实例的属性,类型为字典
__ dir __类或者对象的所有成员名称列表

dir() 函数就是调用__dir__()。
查看属性 :
1). 如果dir([obj]) 参数obj包含方法 __ dir__(),该方法将被调用。
2). 如果Obj 不包含 __ dir__(),该方法将最大限度收集属性信息

二、创建,初始化与销毁

1、__ new __ , __ init__ , __ call __的区别

1). __ new __ 的功能是在生成对象之前执行的内容,接受的参数是cls 类, 负责对象的创建
2). __ init __ 的功能是在生成对象之后执行的内容, 接受的参数是self 对象, 负责对象的初始化
3).__ call __的功能是在调用对象时执行的内容, 可以模拟函数的行为

2、创建对象的步骤

当我们新建一个对象 x=someclass() 的时候,经历的步骤:
1). __new__先创建类并返回类的实例
2). 自动调用__init__来初始化函数的值。
3). 汇总: 第一步和第二步共同构成了构造函数。
4). 对象生命周期调用结束时, __ del __ 方法(构析函数)会被调用。

class Person(object):
    def __new__(cls):
        print("__new__")
        return  object.__new__(cls) #要返回一个对象

    def __init__(self):
        print("__init__")

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

    def __del__(self):
        # 析构方法: 当对象被删除或者从内存释放时自动执行
        print("__del__")
p1 = Person()
p1()

运行结果
在这里插入图片描述

3、关于__ call __魔术方法的一些案例

(1)__ call __ 魔术方法实现缓存

"""__call__ 魔术方法实现缓存"""

from functools import lru_cache

class Fib(object):
    @lru_cache(maxsize=1000)
    def __call__(self, n):
        if n in (1,2):
            return 1
        else:
            return self(n - 1) + self(n - 2)   # 调用对象: self()

if __name__ == '__main__':
    fib = Fib()
    print(fib(20))

(2)__ call __魔术方法实现类装饰器

"""
__call__魔术方法实现类装饰器
在需要传参数时,用类装饰器实现更异读,
把要传的参数写在__init__函数里
装饰的内容写在__call__函数里
"""


from functools import wraps
import time

# 之前传统的装饰器
def timeit(unit):
    def wrapper1(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            if unit == 's':
                start_time = time.time()
                result = func(*args, **kwargs)
                end_time = time.time()
                print("%s函数运行的时间是: %.2f s" %(func.__name__, end_time-start_time))
                return result
            else:
                print('当前功能不支持')
        return wrapper
    return wrapper1

#call方法实现 类装饰器
#将要传入的参数,定义在__init__函数里,装饰的代码写在__call__函数里
class Timeit(object):
    def __init__(self, unit):
        self.unit = unit
    def __call__(self,func):                        # 2
        @wraps(func)
        def wrapper(*args, **kwargs):              #  4
            if self.unit == 's':
                start_time = time.time()
                result = func(*args, **kwargs)
                end_time = time.time()
                print("%s函数运行的时间是: %.2f s" % (func.__name__, end_time - start_time))
                return result
            else:
                print('当前功能不支持')
        return wrapper

# #用传统装饰器装饰函数add
# @timeit(unit='h')     #语法糖: add = timeit(add) 执行timeit() 返回wrapper1  所以 add=wrapper1
# def add(num1,num2):
#     time.sleep(0.333)
#     return num1 + num2

#用类装饰器装饰函数add
@Timeit(unit = 's')                            # 1
# 类名后面跟括号:Timeit() ,是在实例化对象,实例化出一个 object ,
# @object 在语法糖里:add = object(add) ,是在调用对象,执行__call__函数里的内容:进行装饰 返回wrapper , 即 add=wrapper
def add(num1,num2):
    time.sleep(0.333)
    return num1 + num2

if __name__ == '__main__':
    add(1,2)                                   #  3    这里 add=wrapper ,实质是执行 wrapper 函数

(3)__ call __ 魔术方法实现偏函数

"""
偏函数: 冻结预先确定的数
"""
from functools import partial

class partial:
    def __new__(cls, func, *args, **kwargs):
        if not callable(func):  # callable() 函数用于检查一个对象是否是可以调用的
            raise TypeError("没有__call__魔术方法")
        # 实例化对象,获取对象信息
        self = super().__new__(cls) # 调用父类的new方法创建对象
        self.func = func
        self.args = args
        self.krgs = kwargs
        return self         # new函数一定要返回一个对象

    def __call__(self, *args, **kwargs):
        return self.func(*self.args, *args, **self.kwargs, **kwargs)

    max100 = partial(max , 10, 20 , 100)   # 实例化对象
    max_num = max100(1,2,3,4)              # 调用对象
    print(max_num)     # 100

(4)__ call __魔术方法实现函数式编程

Python中的函数式编程功能,如map()、filter()、reduce(),可以使用可调用对象。下面是调用对象和filter()根据文件名扩展名过滤文件。

"""call 和 filter 实现文件过滤器"""

import os

# 父类
class FileAccceptor(object):
    def __init__(self,accepted_extensions):
        """
        :param accepted_extensions:接收的扩展名,例如: .jpg .png
        """
        self.accepted_extensions = accepted_extensions

    def __call__(self,filename):
        """
        :param filename:需要判断的文件名
        :return:
        """
        base, ext = os.path.splitext(filename)  # 切割文件名
        return ext in self.accepted_extensions  # 判断后缀名是否在

#子类
class ImageFileAcceptor(FileAccceptor):
    def __init__(self):
        image_ext = ('.jpg', '.jepg', '.png')
        super(ImageFileAcceptor, self).__init__(image_ext)  # 重写父类init方法
    # 继承父类的__call__方法

class ExcelFileAcceptor(FileAccceptor):
    def __init__(self):
        excel_ext = ('.xls', '.xlsx')
        super(ExcelFileAcceptor, self).__init__(excel_ext)
    # 继承父类的__call__方法

if __name__ == '__main__':
    filenames = [
        'hello.jpg',
        'hello.xls',
        'hello.txt'
    ]

    image_acceptor = ImageFileAcceptor()
    excel_acceptor = ExcelFileAcceptor()
    #使用filter() 进行筛选
    image_file = filter(image_acceptor, filenames)  # 写对象名就行,不用加括号,他内置的自动会调用对象 ,就像之前写的函数,也是只写了函数名,没有加括号
    excel_file = filter(excel_acceptor, filenames)
    # 可以简写为:
    # image_file = filter(ImageFileAcceptor(),filenames)   # 将filenames 一个个传给实例化出的对象 obj('hello.jpg'),调用call
    # excel_file = filter(ExcelFileAcceptor(), filenames)

    print(list(image_file))
    print(list(excel_file))

三、可视化

str() 与repr() 都是python中的内置函数,是直接用来格式化字符串的函数。
当使用内置函数str(obj)时, 自动执行 obj.__ str __ () 魔术方法。
当使用内置函数repr(obj)时, 自动执行 obj.__ repr __ () 魔术方法。
当__str__魔术方法不存在时, 自动执行 obj.__ repr __ () 魔术方法的内容。

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

    # def __str__(self):
    #     return  'Person<%s>' %(self.name)
    def __repr__(self):
        return  'Person<%s>' %(self.name)

p1 = Person("张三", '20')
print(p1)

运行结果:
在这里插入图片描述
如果没有加可视化,运行结果为一个类对象,这样的展示太不友好:
在这里插入图片描述
所以在类中定义了__ str __ 或者__ repr __ 魔术方法,就可以友好展示出我们想要的格式

四、类型转换

魔术方法描述
__ int __转换成整形
__ long __转换成长整型
___ float __转换成浮点型
__ comples __转换成复数型
__ oct __转换成八进制
__ hex __转换成十六进制
__ index __如果定义了可能会用来做切片操作的数值型,可以定义它

类中定义了这些类型转换的魔术方法时,才可以使用 int()、float() 等进行数值类型转换

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

    def __int__(self):    # 定义__int__魔术方法 转换成int型
        return int(self.age)

p1 = Person("张三", '20')
print(p1.age, type(p1.age))
p = int(p1.age)         # 进行数据类型转换
print(p , type(p))

五、容器操作

1、索引与切片

(1)拓展小知识: slice() 函数实现切片对象,主要用在切片操作函数里的参数传递,我们所使用的li[:]实现切片实际上是slice()函数在操作
在这里插入图片描述
(2)实现索引和切片的魔术方法:

__ setitem __:当属性被以索引、切片方式赋值的时候会调用该方法
__ getitem __:一般如果想使用索引、切片访问元素时,就可以在类中定义这个方法
__ delitem __:当使用索引、切片删除属性时调用该方法

2、重复,连接和成员操作符

重复:__ mul __
连接:__ add __
成员操作符:__ contains __

3、循环

转换为迭代器:__iter __
可以使用for循环访问

class Student(object):
    def __init__(self, name, scores):
        self.name = name
        self.scores = scores

    def __getitem__(self, index):
        """获取索引和切片"""
        return self.scores[index]
    def __setitem__(self, index, value):  # 需要传入修改的值
        """修改索引和切片的值"""
        self.scores[index] = value
    def __delitem__(self, index):
        """删除索引和切片的值"""
        del self.scores[index]
    def __mul__(self, other):
        """重复操作"""
        return self.scores * other
    def __add__(self, other):
        """连接操作,传入的是对象"""
        return [item[0]+item[1] for item in zip(self.scores, other.scores)]
    def __contains__(self, item):
        """成员操作符"""
        return item in self.scores
    def __iter__(self):
        """iter可以将可迭代对象转换成迭代器,实现for循环(可以调用next方法)"""
        return iter(self.scores)

if __name__ == '__main__':
    s1 = Student('张三', [80, 90, 100])
    s2 = Student('李四', [30, 30, 30])
    # 索引和切片
    print(s1[0])
    print(s1[1:])
    s1[1:] = [30, 30]
    print(s1.scores)
    del s1[-1]
    print(s1.scores)
    #重复,连接,成员操作符
    print(s1 * 3)
    print(s1 + s2)
    print(200 in s1)
    #循环
    for item in s1:
        print(item)

六、比较大小

class Int(object):
    def __init__(self, number, weight):
        self.number = number
        self.weight = weight

    def __gt__(self, other):
        """判断大于的魔术方法"""
        return  self.number * self.weight > other.number * other.weight

    def __ge__(self, other):
        """判断大于等于的魔术方法"""
        return  self.number * self.weight >= other.number * other.weight

    def __eq__(self, other):
        """判断等于的魔术方法"""
        return self.number * self.weight == other.number * other.weight


i1 = Int(20, 3)
i2 = Int(20, 3)
print(i1 > i2)    # False
print(i1 < i2)    # False
print(i1 >= i2)   # True
print(i1 == i2)   # True
print(i1 != i2)   # False

七、with语句安全上下文

面试题: 我们在进行文件操作时,常常会用到with语句,with语句操作的对象必须是上下文管理器。那么,到底什么是上下文管理器呢?

(1)简单的理解,拥有 __ enter __() 和 __ exit __() 方法的对象就是上下文管理器。
__ enter __(self):进入上下文管理器自动调用的方法,在 with 执行之前执行。如果 有 as子句,该 方法的返回值被赋值给 as 子句后的变量;该方法可以返回多个值。
__ exit __(self, exc_type, exc_value, exc_traceback):退出上下文管理器自动调用的方法。在 with 执行之后执行(不管有无异常)。

(2)当 with as 操作上下文管理器时,就会在执行语句体之前,先执行上下文管理器的 __ enter __() 方法, 然后再执行语句体,最后执行 __ exit __() 方法。

实现上下文管理器

方法一: 魔术方法实现

class Connect(object):
    def __init__(self, filename):
        self.filename = filename
    # 定义__enter__  魔术方法 进入    
    def __enter__(self):
        self.f = open(self.filename)
        return  self.f
    # 定义__exit__ 魔术方法  关闭
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with语句执行之后......')
        self.f.close()

# Connect就是上下文管理器。 拥有 __enter__() 和 __exit__() 方法的对象就是上下文管理器
with Connect('/etc/passwd') as conn:
    pass

方法二:装饰器实现

"""
一个上下文管理器的装饰器 : @contextlib.contextmanager
定义自己所需的基于生成器的上下文管理器
"""
import  contextlib
import  tempfile
import  shutil

@contextlib.contextmanager
def make_temp_dir():
    try:
        tmp_dir = tempfile.mkdtemp()    # 创建临时目录
        yield  tmp_dir
    finally:
        shutil.rmtree(tmp_dir)     # 删除目录
        
with make_temp_dir() as f:
    pass
Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值