面向对象高级特性之--魔术方法

一.定义

在python中,所有用“__”包起来的方法,都统称为“magic method”,中文称为【魔术方法】,例如类的初始化方法 _ _ init_ _ 就是一个魔术方法。

二.特殊属性

python中有几个比较容易理解的魔术方法,是类里面固有的几个特殊属性,总结如下:

魔术方法名称功能
name类、函数、方法的名字
module类定义所在的模块,若不是当前模块,则返回真实的模块
class对象或类所属的类
bases当前类的基类(父类)
doc类、函数的文档帮助,没有定义返回None
mro方法解析顺序,即当前对象继承类的顺序
dict类或实例的属性,可写的字典

编写一个程序,实现这些魔术方法:

from collections import Counter
class A(object):
    pass
class C(object):
    pass
class Date(A,C):
    """
    时间类的封装
    """
    country='China'
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
print(Date.__name__)

#类定义所在的模块,如果在当前脚本,返回__main__,否则,返回真实的模块名eg:collections
print(Date.__module__)
print(Counter.__module__)

#对象或类所属的类,是由哪个类实例化出来的
d=Date(2011,10,2)
print(d.__class__)
print(Date.__class__)

#当前类的基类
print(Date.__bases__)
print(A.__bases__)
print(object.__bases__)

#类、函数的文档帮助,没有定义为None
print(Date.__doc__)

#方法解析顺序,继承顺序,实质上是显示类的继承顺序,python3中多继承算法是广度优先
print(Date.__mro__)
#类或实例的属性,可写的字典
print(Date.__dict__ )

结果

Date
__main__
collections
<class '__main__.Date'>
<class 'type'>
(<class '__main__.A'>, <class '__main__.C'>)
(<class 'object'>,)
()
时间类的封装
(<class '__main__.Date'>, <class '__main__.A'>, <class '__main__.C'>, <class 'object'>)
{'__module__': '__main__', '__doc__': '\n    时间类的封装\n    ', 'country': 'China', '__init__': <function Date.__init__ at 0x000001D87E494D08>}

_ _ dir _ _ 查看属性,功能是返回类或对象的所有成员名称列表。
dir()函数就是调用 _ _ dir _ _ ()
如果dir([object])参数obj包含方法_ _ dir _ _ (),该方法将被调用;如果obj 不包含_ _ dir _ _ (),该方法将最大限度收集属性信息。
dir(object) 对于不同类型的对象obj具有不同的行为:
1.如果对象是模块对象,返回的列表包含模块的属性名和变量名
2.如果对象是类型或类对象,返回的列表包含类的属性名以及它基类的属性名
3.如果obj不写,即dir(),返回列表包含内容不同:(1)在模块中,返回模块的属性和变量名 (2)在函数中,返回本地作用域的变量名 (3)在方法中,返回本地作用域的变量名

程序实现它的使用方法:

class A(object):
    pass
class C(object):
    pass
class Date(object):
    """
    时间类的封装
    """
    country='china'
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
print(dir(Date))
print(dir())

def add(x,y):
    print(dir())
    return x+y
add(1,2)

结果

['__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__', 'country']
['A', 'C', 'Date', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
['x', 'y']

三.对象的创建、初始化、调用、销毁

Python中对象的创建、初始化、调用、销毁分别使用_ _ new _ _ 、_ _ init _ _ 、_ _ del _ _ 、_ _ call _ _ 这四种魔术方法实现

(1).四种魔术方法的功能

  • _ _ new _ _ 的功能是在创建对象前所作的动作,接受的参数是cls类,负责对象的创建。通过new魔术方法,返回一个对象
  • _ _ init _ _ 的功能是在创建对象后,初始化对象,将实例与类属性捆绑,接受的参数是self对象,负责对象的初始化。
  • _ _ call_ _ 的功能是调用对象时执行,可以模拟函数的行为。
  • _ _ del _ _ 为析构方法,对象删除时,自动调用。注意:当程序运行结束后,自动释放变量信息,自动调用析构函数。
  • new方法和init方法共同构成构造函数

(2)四种魔术方法的应用案例

1)一段程序,展示三种魔术方法的实现:
class Student(object):
    def __new__(cls, name):
        print('正在创建对象。。。')
        return super(Student,cls).__new__(cls)  #返回父类的new方法,查看new方法做了什么
    def __init__(self,name):
        print('正在初始化对象。。。。')
        self.name=name
    def __del__(self):
        print('正在删除对象。。。。')
        #当程序结束时,自动释放变量信息,调用析构函数,删除变量
st1=Student('张三')
print(st1.name)

运行结果

正在创建对象。。。
正在初始化对象。。。。
张三
正在删除对象。。。。

析构函数在定义是不执行,只用程序结束时,自动调用析构函数,释放变量信息

2)call魔术方法实现缓存(Fib数列的缓存)
from functools import lru_cache
class Fib(object):
    @lru_cache(maxsize=1000)  #maxsize代表缓存的内存占用值,超过这个值之后,旧的结果就会被释放,然后将新的计算结果进行缓存,其值应当设为2的幂
                              #还有一个参数typed=True/False 等于True时表示把不同类型的参数结果分开保存
    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

当定义一个call方法后,实例化对象后,调用时使用对象加(),即fib(),这样直接调用类的对象,就是调用__call__方法。也可以写为:fib.call(10)
@lru_cache(maxsize,typed) :可以将函数计算结果保存起来,作为缓存,在重复计算时直接返回计算结果,减少运行时间。maxsize参数代表缓存的内存占用值,缓存超过这个值时,旧的结果会被释放,将新的结果进行缓存,其值应当设为2 的幂;typed的值为布尔值,代表是否把不同参数类型的结果分开保存。

3)call魔术方法实现类装饰器

装饰器模式时经常使用的一种python设计模式,一般用于函数实现,但是当函数逻辑很复杂,使用装饰器会让函数冗长,需要把每个小的功能抽象,再进行组合。
类装饰器可以解决这个问题。

from functools import wraps
import time
def timeit(unit='s'):
    def wrapper1(fun):   #类装饰器需要传入参数时,在原来的装饰器函数外面再加一层定义函数
        @wraps(fun)   #语法糖,装饰器wraps为函数fun增加一个wrapper定义的功能
        def wrapper(*args,**kwargs):
            if unit=='s':
                start_time=time.time()
                result=fun(*args,**kwargs)
                end_time=time.time()
                print('%s函数运行的时间为%.f s'%(fun.__name__,end_time-start_time))
            else:
                print('当前功能不支持')
        return wrapper
    return wrapper1


#被装饰的函数在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)
                end_time=time.time()
                print('%s当前函数运行时间为%.1f s'%(fun.__name__,end_time-start_time))
            else:
                print('当前功能不支持')
        return wrapper

@Timeit(unit='s')
def add(num1,num2):
    time.sleep(0.33)
    return num1+num2

add(2,4)

运行结果

add当前函数运行时间为0.3 s
4)call魔术方法实现偏函数

偏函数partial 通过from functools import partial

  • partial(func,*args,**kwargs) , *args是元组,**kwargs也是元组
  • 通过partial函数将func函数计算的结果(计算的输入值为*args参数)保存在一个变量中,接下来直接使用这个存有预先计算结果的变量作为函数,计算先前计算的结果与新输入的参数的新的结果
class partial(object):
    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)
min_num=partial(min,10,11)
print(min_num(2,3))

运行结果

2

callable()函数用于检查一个兑现是否可以调用

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

python中函数式编程功能,如map(),filter(),reduce()

import os
def is_odd(x):
    return x%2==1
list1=list(map(lambda x:x**2,[1,2,3,4])) #map()函数,根据给定的函数,对指定的序列做映射
list2=list(map(lambda x,y:x+y,[1,2,3,4],[2,3,4,5]))
list3=list(filter(is_odd,[1,2,3,4]))
print(list1,list2,list3)

import os
class FileAcceptor(object):
    def __init__(self,accepted_extendions):
        """
        eg:['.png','.jpg']
        @param accepted_extendions:可接受的文件扩展
        """
        self.accrpted_extensions=accepted_extendions
    def __call__(self, filename):
        """
        eg:hello.png
        @param filename:需要判断的文件名
        @return:
        """
        #base='base',ext='.jpg'
        base,ext=os.path.splitext(filename)
        return ext in self.accrpted_extensions
class ImageFileAccpetor(FileAcceptor):
    def __init__(self):
        image_ext=('.jpg','jepng','.png')
        super(ImageFileAccpetor, self).__init__(image_ext)
class ExcelFileAcceptor(FileAcceptor):
    def __init__(self):
        excel_ext=('.xls','.xlsx')
        super(ExcelFileAcceptor, self).__init__(excel_ext)
if __name__ == '__main__':
    filename=[
        'hello.jpg',
        'hello.xls',
        'hello.txt'
    ]
    image_file=filter(ImageFileAccpetor(),filename)
    excel_file=filter(ExcelFileAcceptor(),filename)
    print(list(image_file))
    print(list(excel_file))

运行结果

[1, 4, 9, 16] [3, 5, 7, 9] [1, 3]
['hello.jpg']
['hello.xls']

四.可视化

  • str()和repr()都是python中的内置函数,是直接用来格式化字符串的函数
  • 当使用内置函数str(obj)时,自动执行obj._ _ str _ _ ()魔术方法
  • 当使用内置函数repr(obj)时,自动执行obj. _ _ repr _ _ ()魔术方法
  • 当 _ _ str _ _ 魔术方法不存在时,自动执行_ _ repr_ _()魔术方法的内容
class Person(object):
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __ini__(self):
        return int(self.age)
    def __repr__(self):
        return 'Person<%s>'%(self.name)
p1=Person('fentiao','100')
print(p1)
print(int(p1.age))

运行结果

Person<fentiao>
100

五 .类型转换

魔术方法名称功能
_ _ int _ _(self)转化成整型
_ _ long _ _(self)转化成长整型
_ _ float_ _(self)转化成浮点型
_ _ complex _ _(self)转化成复数型
_ _ oct_ _(self)转化成八进制
_ _ hex_ _ (self)转化成十六进制
_ _ index_ _(self)如果定义了一个可能被用来做切片操作的数值类型,就需要定义index
_ _ trunc_ _(self)当math.trunc(self)使用时被调用_ _ trunc_ _返回自身类型的整型截取
_ _ coerce_ (self,other) _执行混合类型的运算

六.索引和切片

slice()函数实现切片对象,主要用在切片操作函数里的参数传递

  • _ _ 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]
stu1=Student('张三',[100,98,96])
stu2=Student('李四',[76,97,86])
print(stu1[1:])
stu1[1:]=[99,99]
print(stu1.scores)
del stu1[1:]
print(stu1.scores)

for i in stu1.scores:
    print(i)

运行结果

slice(1, None, None)
[98, 96]
[100, 99, 99]
[100]
100

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

  • _ _ mul _ _():重复操作符,实现*的功能,具体返回什么取决于代码的业务要求
  • _ _ add_ _ ():连接操作符,连接的必须是同一种数据类型
  • _ _ contains _ _ ():成员操作符,判断某个元素是否存在于这个对象中
class Student(object):
    def __init__(self,name,scores):
        self.name=name
        self.scores=scores
    def __mul__(self, other):
        """重复操作符"""
        print('重复中')
        return self.scores*other
    def __add__(self, other):
        """连接操作符"""
        print('连接中')
        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,98,96])
stu2=Student('李四',[76,97,86])

print(stu1*3)
print(stu1+stu2)
print(150 in stu2)

运行结果

重复中
[100, 98, 96, 100, 98, 96, 100, 98, 96]
连接中
[176, 195, 182]
False

八.循环

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

    def __iter__(self):
        """iter可将可迭代对象转化为迭代器(可以调用next方法)"""
        return iter(self.scores)

stu1 = Student('张三', [100, 98, 96])
stu2 = Student('李四', [76, 97, 86])

for item in stu2.scores:
    print(item)

运行结果

76
97
86

九.with语句安全上下文

with语句操作的对象必须是上下文管理器。

1.什么是上下文管理器

(1).简单的理解,拥有_ _ enter_ _() 和 _ _ exit _ _()方法的对象就是上下文管理器

  • 首先 _ _ enter _ _ (self) :进入上下文管理器自动调用的方法在with执行之前运行。如果有as子句,该方法的返回值被赋值给as子句后的变量;该方法可以返回多个值。
  • 然后 _ _ exit _ _(self,exc_type,exc_value,exc_traceback):退出上下文管理器自动调用的方法在with之后执行。
    (2).执行顺序
  • 当with as操作上下文管理器时,就会在执行语句体前,先执行上下文管理器的 _ _ enter _ _()方法
  • 然后在执行语句体后,最后执行 _ _ exit _ _()方法

2.构建上下文管理的方法

(1).基于生成器

装饰器context.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

(2)基于类的上下文管理器

只要一个类实现了_ _ enter _ _() 和 _ _ exit _ _()这两个方法,程序就可以使用with as 语句来管理它。

class Connect(object):
    def __init__(self,filename):
        self.filename=filename
    def __enter__(self):
        self.f=open(self.filename)
        return self.f
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with语句执行之后')
        self.f.close()
#connect就是上下文管理器。拥有__enter__()和__exit__()方法的对象就是上下文管理器
with Connect('C:/用户/79291/桌面/1_2019.12.13电子三路看房情况.doc') as conn:
    pass

十.魔术方法汇总

比较大小

魔术方法名称功能
_ _ gt _ _ (self,other)判断大小
_ _ ge _ _ (self,other)判断大于等于
_ _ eq _ _ (self,other)判断等于

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

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值