python初学面向对象之魔术方法

魔术方法

  • Python中,所有用"__"包起来的方法,都称为【魔术方法】(eg: __len__, __init__)
  • 魔术方法一般是为了让显示器调用的,你自己并不需要调用它们。

一、 特殊属性

1.下面便是python中常见的魔术方法和其功能:

 
魔术方法名称               功能
__name__           类、函数、方法等的名字
__module__           类定义所在的模块
__class__           对象或类所属的类
__bases__           当前类的基类(父类)
__doc__
          类、函数的文档帮助,没有定义为 None
__mro__          Method Resolution Order 方法解析顺序
__dict__          类或实例的属性,可写的字典

部分饰演效果如下:

python初学面向对象之魔术方法_第1张图片

 
python初学面向对象之魔术方法_第2张图片
 
2 查看属性
  • __dir__ 返回类或者对象的所有成员名称列表。
  • dir() 函数就是调用__dir__()
1). 如果dir([obj]) 参数 obj 包含方法 __dir__() ,该方法将被调用。
 
2). 如果 Obj 不包含 __dir__() ,该方法将最大限度收集属性信息
 

二、创建,初始化与销毁

 
1、 python 中__new__ , __init__ , __call__的区别?
 
 
1). new 的功能是在 生成对象之前执行的内容 ,接受的参数是 cls , 负责对象的创建;
 
 
2). init 的功能是在 对象生成之后执行的内容 , 接受的参数是 self 对象 , 负责对象的初始化;
 
 
3). call 的功能是 在调用对象时执行的内容 , 可以模拟函数的行为 .
 
 
2、当我们新建一个对象 x=someclass() 的时候,经历的步骤:
 
 
1). 第一 : __new__ 先创建类并返回类的实例。
 
 
2). 第二 : 自动调用 __init__ 来初始化函数的值。
 
 
3). 汇总 : 第一步和第二步共同构成了【构造函数】。
 
 
4). 第三步 : 对象生命周期调用结束时, __del__ 方法 ( 构析函数 ) 会被调用。
 
 
3、创建,初始化与销毁应用范例: new魔术方法实现单例模式
 
1)应用范例一 : call 魔术方法实现缓存(Fib数列的缓存)
 
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)
fib = Fib()
print(fib(10)) 

测试结果:

55

 

2)应用范例二 : call 魔术方法实现类装饰器
 
装饰器模式是经常使用的一种 Python 设计模式,也非常的好用,一般是用函数实现,但是这种实现有一个缺点。
如果逻辑非常的复杂,写在一个函数中,会让函数非常长且冗余,需要把小功能的抽象,然后再进行组合 而类装饰器,就适用于这种场景。
from  functools import  wraps
import time
def timeit(unit='s'):
    def wrapper1(fun):  # fun=add
        @wraps(fun)
        def wrapper(*args, **kwargs):
            if unit == 's':
                start_time = time.time()
                result = fun(*args, **kwargs)       # add(1, 2)  result=3
                end_time = time.time()
                print("%s函数运行时间为%.2f s" %(fun.__name__, end_time-start_time))
                return  result
            else:
                print("当前功能不支持......")
        return  wrapper
    return  wrapper1


# 类装饰器: 装饰器需要传递的参数通过__init__传递进入.被装饰函数执行的内容在__call__魔术方法中编写。
class TimeIt(object):
    def __init__(self, unit='s'):
        self.unit = unit
    def __call__(self,fun):
        @wraps(fun)
        def wrapper(*args, **kwargs):
            if self.unit == 's':
                start_time = time.time()
                result = fun(*args, **kwargs)  #add(1, 2)   result=3
                end_time = time.time()
                print("%s函数运行时间为%.2f s" % (fun.__name__, end_time - start_time))
                return result
            else:
                print("当前功能不支持......")
        return  wrapper


# @timeit(unit='s')       # @wrapper1 ==> add = wrapper1(add)  ===> add = wrapper
# def add(num1, num2):
#     time.sleep(0.333)
#     return  num1 + num2


"""
@TimeIt(unit='h') 
# 1). TimeIt_obj = TimeIt(unit='h')  
# 2). @TimeIt_obj
# 3). add=TimeIt_obj(add)
# 4). add = wrapper
"""
@TimeIt(unit='s')
def add(num1, num2):
    time.sleep(0.333)
    return  num1 + num2

add(1, 2)
 
3)应用范例三 : call 魔术方法实现偏函数
 
偏函数 (2.5+) partial function
通过有效地“冻结”预先确定的参数,来缓存函数参数。
运行时获得剩余参数后,解冻并传递到函数中。
系统内置的偏函数操作范例如下:
 
from functools import  partial

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

测试结果:

100

4)应用范例四: call魔术方法实现函数式编程

 
  • Python中的函数式编程功能,如map()filter()reduce(),可以使用可调用对象。下面是使用可调用对象和filter()根据文件名扩展名过滤文件的代码。
  • 这种编程思维在Flask框架和Django框架的表单处理源代码中最为常见。
  • 测试代码如下:
class partial:
    def __new__(cls,func,*args,**kwargs):
        if not callable(func):
            raise TypeError("the first argument must be callable")
        self = super().__new__(cls)
        self.func = func
        self.args = args
        self.kwargs = kwargs
        return self
    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

 注意:calable() 函数用于检查一个对象是否是可以调用的(有没有__call__模式方法)。 

 

三、 可视化  

 
类型判断要使用 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("fentiao", '100')
print(p1)
print(int(p1))
测试结果:
 
Person
100

 

四、 类型转换

 
magic method explain
__init__(self) 转换成整形
__long__(self) 转换成长整形
__float__(self) 转换成浮点型
__complex__(self) 转换成复数型
__oct__(self) 转换成八进制
__hex__(self) 转换成十六进制
__index__(self)

如果定义了一个可能被用来做切片操作的数值类型,

就应该定义index

__trunc__(self) 当math.trunc(self)使用时被调用__trunc__返回自身类型的 整型截取
__coerce__(self,other) 执行混合类型的运算

 

 

六、索引与切片

 
注:  slice() 函数实现切片对象,主要用在切片操作函数里的参数传递。
 
如:
 
python初学面向对象之魔术方法_第3张图片
 
 
索引&切片魔术方法:
 
 
__setitem__ :当属性被以索引、切片方式赋值的时候会调用该方法
 
 
__getitem__ :一般如果想使用索引、切片访问元素时,就可以在类中定义这个方法
 
 
__delitem__ :当使用索引、切片删除属性时调用该方法
 
如:
 
class Student(object):
    def __init__(self, name, scores):
        self.name = name
        self.scores = scores

    def __getitem__(self, index):
        """实现获取索引和切片值的魔术方法"""
        print(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可以将可迭代对象转换成迭代器(可以调用next方法的)
        return  iter(self.scores)

stu1 = Student("张三", [100, 90, 100])
stu2 = Student("李四", [100, 80, 100])

#1). 索引和切片的测试
print(stu1[1:])             # 获取索引/切片值
stu1[1:] = (80, 80)        # 设置索引/切片对应的value值
print(stu1.scores)
del stu1[1:]                # 删除索引/切片值
print(stu1.scores)

for item in stu1:
    print(item)

测试结果:

slice(1, None, None)
[90, 100]
[100, 80, 80]
[100]
100

 

七、 重复,连接与成员操作符

1)__mul__() :重复操作符,实现*的效果,具体返回什么取决于代码的业务需求;
2)__add () :连接操作符,连接的时候必须是同一种数据类型;
3)__contains__() :成员操作符,判断某个元素是否存在于这个对象中。
 
例如:
 
class Student(object):
    def __init__(self, name, scores):
        self.name = name
        self.scores = scores

    def __getitem__(self, index):
        """实现获取索引和切片值的魔术方法"""
        print(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可以将可迭代对象转换成迭代器(可以调用next方法的)
        return  iter(self.scores)

stu1 = Student("张三", [100, 90, 100])
stu2 = Student("李四", [100, 80, 100])

# 2)连接、重复和成员操作符
print(stu1*3)
print(stu1 + stu2)
print(150 in stu1)
测试结果:
 
[100, 90, 100, 100, 90, 100, 100, 90, 100]
[200, 170, 200]
False

八、 循环

def __iter__(self): #迭代,使得该对象实现for循环
    #将列表转换为迭代的类型,可以for循环,一定要返回iter类型的数据;
    return iter(self.scores)

#7)实现for循环?
for item in liming:
    print(item)

九、with语句安全上下文

 
注: with语句操作的对象必须是上下文管理器。那么,到底什么是上下文管理器呢?
 
 
1). 简单的理解,拥有 __enter__() __exit__() 方法的对象就是上下文管理器。
__enter__(self):进入上下文管理器自动调用的方法 ,在 with 执行之前执行。如果 有 as 子句,该方法的返回值被赋值给 as 子句后的变量;该方法可以返回多个值。
__exit__(self, exc_type, exc_value, exc_traceback ):退出上下文管理器自动调用的方法 在with 执行之后执行(不管有无异常 )
 
2). 当 with as 操作上下文管理器时,就会在执行语句体之前,先执行上下文管理器的 __enter__() 方法,然后再执行语句体,最后执行 __exit__() 方法。
 
构建上下文管理器,常见的有 2 种方式: 基于类实现和基于生成器实现。
方法一: 装饰器 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

测试结果:

with语句执行之后......

Process finished with exit code 0

方法二: 基于类的上下文管理器: 只要一个类实现了 __enter__() 和 __exit__() 这 2 个方法,程序就可以使用 with as 语句来管理它。

class Myopen(object):
    def __init__(self,name,mode='r'):
        self.name=name
        self.mode=mode
    def __enter__(self):
        #当with语句进入并开始执行时,执行的内容,
        #需要返回一个对象,在执行结束后用来关闭或者其他操作:
        self.f=open(self.name,self.mode)
        print("正在打开文件%s......" %(self.name))
        return self.f
    def __exit__(self,exc_type,exc_val,exc_tb):
        #with语句执行结束后,做什么操作
        self.f.close()
    print("文件正在关闭.....")

 测试结果:

文件正在关闭.....

案例:基于call魔术方法和filter实现文件过滤器

import os
# 作为基类/父类
class FileAcceptor(object):
    def __init__(self, accepted_extensions):
        """
        eg: ['.png', '.jpg']
        :param accepted_extensions: 可以接受的扩展名
        """
        self.accepted_extensions = accepted_extensions

    def __call__(self, filename):
        """
        eg: hello.jpg
        :param filename: 需要判断的文件名
        :return:
        """
        # base = 'hello', ext='.jpg'
        base, ext = os.path.splitext(filename)
        return  ext in self.accepted_extensions


# 子类
class ImageFileAcceptor(FileAcceptor):
    def __init__(self):
        image_ext = ('.jpg', '.jepg', '.png')
        super(ImageFileAcceptor, self).__init__(image_ext)


# 子类
class ExcelFileAcceptor(FileAcceptor):
    def __init__(self):
        image_ext = ('.xls', '.xlsx')
        super(ExcelFileAcceptor, self).__init__(image_ext)

if __name__ == '__main__':
    filenames = [
        'hello.jpg',
        'hello.xls',
        'hello.txt'
    ]
    """
    1). ImageFileAcceptor() 实例化对象, 执行__new__和__init__魔术方法。
    2). imagefileacceptor_obj
    3). imagefileacceptor_obj('hello.jpg')  True
    3). imagefileacceptor_obj('hello.xls')  False
    3). imagefileacceptor_obj('hello.txt')  False
    4). ['hello.jpg']
    
    """
    images_file = filter(ImageFileAcceptor(), filenames)
    excels_file = filter(ExcelFileAcceptor(), filenames)
    print(list(images_file))
    print(list(excels_file))

测试结果:

['hello.jpg']
['hello.xls']

十、魔术方法汇总

基本的魔法方法
 
有关属性的魔术方法
 
比较操作符
 
算数运算符
 
反运算
 
增量赋值运算
 
一元操作符
 
类型转换
 
上下文管理( with 语句)
 
容器类型
 
 
python初学面向对象之魔术方法_第4张图片
 
 
 

你可能感兴趣的:(python初学面向对象之魔术方法)