【python】42_面向对象编程中的魔术方法

在Python中,所有用"__"包起来的方法,都称为【魔术方法】(eg: __ len __, __ init __)。

1. 特殊属性

在这里插入图片描述
1.__ name __
在这里插入图片描述
2.__ module __
在这里插入图片描述
在这里插入图片描述
3. __ class __
在这里插入图片描述
4.__ bases __
在这里插入图片描述
5.__ doc __
没有定义帮助文档的话,则为None
在这里插入图片描述
6. __ mro __
多继承里可以查看继承的搜索顺序

7.__ dict__
在这里插入图片描述
8.__ dir__ 返回类或者对象的所有成员名称列表。 查看属性
dir() 函数就是调用__ dir __()。
查看属性
1). 如果dir([obj]) 参数obj包含方法 dir(),该方法将被调用。
2). 如果Obj 不包含 dir(),该方法将最大限度收集属性信息

2.创建,初始化与销毁

面试常问: python 中__new__ , __ init__ , __call__的区别?
1). new的功能是在生成对象之前执行的内容,接受的参数是cls 类, 负责对象的创建
2). init的功能是在对象生成之后执行的内容, 接受的参数是self 对象, 负责对象的初始化
3). call的功能是在调用对象时执行的内容, 可以模拟函数的行为.

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

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

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()

首先实例化出来一个p1对象,因此首先执行new给对象开辟空间返回一个对象,其次执行init初始化对象。再调用p1(),调用对象时会执行call函数。程序结束后,内存释放,自动调用了析构函数del。

在这里插入图片描述

2.1 new魔术方法实现单例模式

在上一篇博文最后已经介绍过了

2.2 call魔术方法实现缓存

from functools import lru_cache

class Fib(object):
    @lru_cache(maxsize=1000)
    def __call__(self, num):
        if num in (1,2):
            return 1
        else:
            return self(num-1)+self(num-2)

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

???和直接用@lru_cache装饰器没区别啊???
还有其他实现方式吗???

2.3 call魔术方法实现类装饰器

这里我们还是以计算程序运行时间为例来写装饰器:

from functools import wraps
import time

用函数实现的装饰器如下:

def timeit(func):
    @wraps(func)
    def wrapper(*args,**kwargs):
        start = time.time()
        result = func(*args,**kwargs)
        end = time.time()
        print('%s执行了%.3f' %(func.__name__,end-start))
        return result
    return wrapper

@timeit
def count(n):
    while n>0:
        n-=1

调用一下看结果:

if __name__ == '__main__':
    count(1000000)

在这里插入图片描述
如果要用call魔术方法实现类装饰器,很简单,在这个基础上进行改动。

class timeit(object):
    def __call__(self,func):
        @wraps(func)
        def wrapper(*args,**kwargs):
            start = time.time()
            result = func(*args,**kwargs)
            end = time.time()
            print('%s执行了%.3f' %(func.__name__,end-start))
            return result
        return wrapper

@timeit()     #--->timeit实例化出来一个对象timeit_obj 
              # 再用这个count=timeit_obj(count)
def count(n):
    while n>0:
        n-=1

类装饰器的优点体现在当我们要为这个装饰器传递参数时,普通做法是在原有的装饰器外面加一层,但是类装饰器直接加一个__ init__()初始化方法,将参数传递给这个初始化方法,封装起来,在call方法中调用即可。
普通做法:


def timeit(type='s'):
    def wrapper1(func):
        @wraps(func)
        def wrapper(*args,**kwargs):
            if type == 's':
                start = time.time()
                result = func(*args,**kwargs)
                end = time.time()
                print('%s执行了%.3f' %(func.__name__,end-start))
                return result
            else:
                print('尚未开通此功能')
        return wrapper
    return wrapper1

@timeit('h')
def count(n):
    while n>0:
        n-=1

类装饰器的做法:

class timeit(object):
    def __init__(self,type = 's'):
        self.type = type

    def __call__(self,func):
        @wraps(func)
        def wrapper(*args,**kwargs):
            if self.type == 's':
                start = time.time()
                result = func(*args,**kwargs)
                end = time.time()
                print('%s执行了%.3f' %(func.__name__,end-start))
                return result
            else:
                print('尚未开通此功能')
        return wrapper

@timeit('h')     #------>timeit实例化出来一个对象timeit_obj  再用这个count=timeit_obj(count)
def count(n):
    while n>0:
        n-=1

2.4 call魔术方法实现偏函数

偏函数(2.5+) partial function。通过有效地“冻结”预先确定的参数,来缓存函数参数。运行时获得剩余参数后,解冻并传递到函数中。系统内置的偏函数操作范例如下:

from functools import  partial
 
max_100 = partial(max,10, 100)  # 返回对象
print(max_100(1, 2, 3))

在这里插入图片描述
用new方法和call方法对偏函数的实现如下:
在这里插入图片描述

2.5 call魔术方法实现函数式编程

回忆一下map、filter
map:
在这里插入图片描述
filter:
在这里插入图片描述
在这里插入图片描述
Python中的函数式编程功能,如map()、filter()、reduce(),可以使用可调用对象。下面是使用
可调用对象和filter()根据文件名扩展名过滤文件的代码。

import os

class FileAcceptor(object):
    def __init__(self,accepted_extention):
        """
        eg: ['.png', '.jpg']
        :param accepted_extensions: 可以接受的扩展名
        """
        self.accepted_extention = accepted_extention

    def __call__(self, filename):
        """
        eg: hello.jpg
        :param filename: 需要判断的文件名
        :return:
        """
        base, ext = os.path.splitext(filename)
        return ext in self.accepted_extention
    
class ImageFileAcceptor(FileAcceptor):
    def __init__(self):
        image_ext = ('.jpg', '.jepg', '.png')
        super(ImageFileAcceptor, self).__init__(image_ext)

class ExcelFileAcceptor(FileAcceptor):
    def __init__(self):
        excel_ext = ('.xls','.xlsx')
        super(ExcelFileAcceptor, self).__init__(excel_ext)

class TextFileAcceptor(FileAcceptor):
    def __init__(self):
        text_ext = ('.txt','.doc')
        super(TextFileAcceptor, self).__init__(text_ext)

filename = [
    'sdkfj.png',
    'sdkf.jpg',
    'sdfwer.png',
    'lkjl.xls',
    'sdfkhjk.doc'
]
imagefileaccptor = ImageFileAcceptor()
exclefileaccptor = ExcelFileAcceptor()
textfileaccptor = TextFileAcceptor()
print(list(filter(imagefileaccptor,filename)))
print(list(filter(exclefileaccptor,filename)))
print(list(filter(textfileaccptor,filename)))

在这里插入图片描述

3.可视化

类型判断要使用type或isinstance, 不能通过判断print输出是否带引号来判断输出值的类型。
1). str()与repr()都是python中的内置函数,是直接用来格式化字符串的函数。
2). 当使用内置函数str(obj)时, 自动执行obj.str()魔术方法。
3). 当使用内置函数repr(obj)时, 自动执行obj.repr()魔术方法。
4). 当__str__魔术方法不存在时, 自动执行__repr__()魔术方法的内容。

class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age
 
    def __int__(self):
        return int(self.age)
 
    def __repr__(self):
        return  'Person<%s>' %(self.name)
p1 = Person("xiaoming", '20')
print(p1)
print(int(p1))

在这里插入图片描述

4. 类型转换

在这里插入图片描述

5. 索引与切片

注:slice() 函数实现切片对象,主要用在切片操作函数里的参数传递。
在这里插入图片描述
索引&切片魔术方法:
__ setitem__:当属性被以索引、切片方式赋值的时候会调用该方法
__ getitem__:一般如果想使用索引、切片访问元素时,就可以在类中定义这个方法
__ delitem__:当使用索引、切片删除属性时调用该方法

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]

if __name__ == '__main__':
    st1 = Student('xiaoming',[100,80,90,200,180])
    print(st1.scores)
    print(st1[1:4])
    st1[2] = 111
    print(st1.scores)
    del(st1[1])
    print(st1.scores)

在这里插入图片描述

6.重复,连接与成员操作符

class Student(object):
    def __init__(self, name, scores):
        self.name = name
        self.scores = scores
        
    def __mul__(self, other):
        """重复操作"""
        return  self.scores * other

    def __add__(self, other):
        """连接操作, 传入的时对象"""
        return [ item[0]+item[1] for item in zip(self.scores, other)]
        ##也可以 return self.scores+other  (看个人逻辑)

    def __contains__(self, item):
        """成员操作符"""
        return  item in self.scores
if __name__ == '__main__':
    st1 = Student('xiaoming',[100,80,90,200,180])
    print(st1*3)
    print(st1+[1,2,3,4])
    print(100 in st1)

在这里插入图片描述

7.循环

class Student(object):
    def __init__(self, name, scores):
        self.name = name
        self.scores = scores
    def __iter__(self):
        # iter可以将可迭代对象转换成迭代器(可以调用next方法的)
        return  iter(self.scores)

st1 = Student('xiaoming',[100,80,90,200,180])
for item in st1:
    print(item)

在这里插入图片描述

8.比较大小

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)
print(i1 < i2)
print(i1 >= i2)
print(i1 == i2)
print(i1 != i2)

在这里插入图片描述

9.魔术方法总结

基本的魔法方法
有关属性的魔术方法
比较操作符
算数运算符
反运算
增量赋值运算
一元操作符
类型转换
上下文管理(with 语句)
容器类型
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值