python面向对象的魔法方法详解

什么是魔法方法

我们都知道我们在创建一个类的时候python默认是继承与object这个基类的,这个基类里面包含的方法就是我们常说的魔法方法,要想学好python中面向对象的编程,了解这些魔法方法是必要的,接下来我们通过其中的dir()方法,将我们创建类的所有方法打印出来,方便我们研究:


class cesi(object):
	"""这是一个用于测试的类"""
	def __init__(self, name, age):
		super(cesi, self).__init__()
		self.name = name
		self.age = age

	def run(self):
		self.name = "Tom"
		
		
def main():
	t = cesi("Jhon", 18)
	for method in dir(t):
		print(method)

if __name__ == "__main__":
	main()

在其中我们使用了dir()方法,那么他的作用应该很明显了,就是获得我们类的方法列表,包括父类方法

__class__:展示当前对象所属的类及父类
__delattr__:delattr(x, 'foobar') 相等于 del x.foobar

__dict__:以字典的形式展示当前对象的属性

__dir__:返回对象方法的列表

__doc__:描述类的信息

__eq__:拥有__eq__方法的对象支持相等的比较操作

__format__:格式化字符串的方法

__ge__:可比较的类,跟__eq__同属一类内置方法

__getattribute__:获取一个计算属性(无条件的)

__gt__:可比较的类

__hash__:拥有此属性的对象能够进行hash

__init__:创建对象过程进行初始化

__init_subclass__:

__le__:比较属性

__lt__:比较属性

__module__:查看该对象属于哪个模块

__ne__:比较属性

__new__:在对象被创建的时候回自动调用,返回内存空间

__reduce__:

__reduce_ex__:

__repr__:将对象转化为供解释器读取的形式

__setattr__:设置某属性的值

__sizeof__:返回地址

__str__:如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值

__subclasshook__:控制某个类是否是该类的子类

__weakref__:

方法详解

类的基础方法

在这里插入图片描述 1. 初始化一个实例对象,自动调用__init__方法,这个没什么好说的

2.在Pyhton 中,如果想定制化打印出实例对象的信息,那可以使用__str__和__repr__魔法方法来定制化显示。

在Python中,打印一个自定义类的实例对象所显示的信息往往是很不友好的,如下所示:


>>> class Animal():
...     def __init__(self,name):
...             self.name = name
...
>>> dog  = Animal('monkey')
>>> print(dog)
<__main__.Animal instance at 0x0000000003664F88>

str__和__repr 所起的作用就是定制化显示输出信息(你想输出什么就是什么),当然,两者也有区别的,区别如下:

  • 对于print和str内置函数,程序会首先尝试__str__,如果没有__str__,则尝试__repr__,如果没有__repr__,则选用默认显示。

  • 在交互式回应,repr函数中__repr__会被调用,你可以这么认为,str 是给用户看的,__repr__则是给开发者看的. 看下面示例:


>>> class Animal():
...     def __init__(self,name):
...             self.name = name
...     def __str__(self):
...             return "i am %s" % self.name
...     def __repr__(self):
...             return "Animal:name is %s" % self.name
...
>>> dog = Animal('kitty')
>>> dog
Animal:name is kitty
>>> print(dog)
i am kitty
>>> str(dog)
'i am kitty'
>>> repr(dog)
'Animal:name is kitty'
  1. bytes:返回字节型编码,一般在str型中有类似方法,方便产生字节型编码
  2. __format__格式化字符串,在我们使用python的过程中往往会有字符串格式化输出的需求,我们通常用的%方法,或者format方法,使用format方法的前提是我们的类里面封装有format方法,如果没有,其返回的是str方法的返回值。其使用展示如下:

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

    __format_dict = {
        'n-a': '名字是:{obj.name}-年龄是:{obj.age}',  # 名字是:maple-年龄是:18
        'n:a': '名字是:{obj.name}:年龄是:{obj.age}',  # 名字是:maple:年龄是:18
        'n/a': '名字是:{obj.name}/年龄是:{obj.age}',  # 名字是:/年龄是:18
    }

    def __format__(self, format_spec):
        if not format_spec or format_spec not in self.__format_dict:
            format_spec = 'n-a'
        fmt = self.__format_dict[format_spec]
        print(fmt) #{obj.name}:{obj.age}
        return fmt.format(obj=self)

s1 = Student('maple', 24)
ret = format(s1, 'n/a')
print(ret)  # maple/24

我们如果在类中定义这样一个方法,方便我们进行格式化的输出

类之间的比较

在这里插入图片描述
类之间的比较,要使用上述方法进行重新封装,比如代码如下:


class A(object):
    def __init__(self, name, x):
        self.name = name
        self.x = x

    def __repr__(self):
    	return self.name

    def __str__(self):
    	return "I'am right"

    def __eq__(self, obj):
    	if self.name == obj.name :
    		return True
    	else:
    		return False


a = A('zzy', 6)
b = A('zzy', 7)
print(str(a))
print(a == b)

输出结果是显然的,结果为True,其他的方法也是类似,这里就不再一一列举了

迭代器

python中如果想要通过for等方法将内部的元素提取出来,首先对象应该是可迭代的,我们有一些简单的方法能够判断一个对象是否可以迭代:


from collections.abc import Iterable
isintance([11,22,33,44],Iterable)
out: True

如果进一步的我们还需要判断一个对象是否为迭代器,那么采用如下方法:


from collections.abc import Iterator
isintance([11,22,33,44],Iterator)
out: True

到了这里,我们就会想了,什么样的对象是可迭代的,什么样的对象又可以称为一个迭代器呢,我们一步一步来

  • 可迭代对象

只要实现了__iter__方法,那么这个对象就是可迭代的,上代码演示:


import time
from collections.abc import Iterable
from collections.abc import Iterator


class person(object):
    def __init__(self):
        self.name = list()
        self.current_num = 0

    def add(self, name):
        self.name.append(name)

    def __iter__(self):
        return [11,22,33].__iter__()

def main():
    zzy = person()
    print("Whether the object can be iterated: {}".format(isinstance(zzy, Iterable)))
    for i in zzy:
        print(i)

if __name__ == '__main__':
    main()

输出为:

Whether the object can be iterated: True
11

22

33
  • 迭代器
    除了实现__iter__方法外,还要实现__next__方法,这样的对象,我们称为迭代器

import time
from collections.abc import Iterable
from collections.abc import Iterator


class person(object):
    def __init__(self):
        self.name = list()
        self.current_num = 0

    def add(self, name):
        self.name.append(name)

    def __iter__(self):
        return self

    def __next__(self):
        if self.current_num < len(self.name):
            ret = self.name[self.current_num]
            self.current_num += 1
            return ret
        else:
            raise StopIteration

def main():
    zzy = person()
    print("Whether the object can be iterated: {}".format(isinstance(zzy, Iterator)))
    zzy.add("Jhon")
    zzy.add("yinxiaohui")
    zzy.add("DD")
    for i in zzy:
        print(i)

if __name__ == '__main__':
    main()

通过上述方法实现一个简单的迭代器,其输出为:

Whether the object can be iterated: True
Jhon

yinxiaohui

DD
计算属性

在这里插入图片描述上面的方法基本上都是和属性的设置相关的,如果我们希望在设置属性或者更改属性的时候我们还希望去做一些额外的事情,我们只需要对上述方法进行简单的重写就可以了,在这里呢,我们以__setattr__方法为例来进行展示,代码如下:


class person(object):
    def __init__(self, name):
        self.name = name
        self.current_num = 0

    def __setattr__(self, name, value):
        print("This method is called for: {}".format(name))
        #self.__dict__[name] = str(value)
        super(person, self).__setattr__(name, value)


def main():
    a = person("zzy")

if __name__ == '__main__':
    main()

显然,这个方法是简单的,但要注意在重写__setattr__方法的时候要注意不要再调用设置属性,那样的话就会陷入无限循环,这样就不太友好了,其余方法的书写也类似,你要么调用父类方法,要么就通过__dict__方法进行处理,因为本质上,这个父类方法就是调用__dict__方法。

行为方式与函数类似的类

在这里插入图片描述很明显,通过上述的方法描述我们很容易知道,这个方法的作用,但现在比较重要的问题是,这个函数在什么时候会被调用,下面我们测试一下:


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

    def __call__(self):
        print("I was called")


def main():
    a = person("zzy")

if __name__ == '__main__':
    main()

我们发现,在进行对象创建过程中,属性并没有被调用,那什么时候方法会被调用呢?


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

    def __call__(self):
        print("I was called")


def main():
    a = person("zzy")()

if __name__ == '__main__':
    main()

这时候,我们发现我们的call方法被调用了,也就是说,如果我们想要去调用call方法,需要在后面再加一个传入函数参数的括号

行为方式与序列类似的类

在这里插入图片描述

行为方式与字典相似的类

在这里插入图片描述


class Foo(object):
 
    def __getitem__(self, key):
        print ('__getitem__',key)
 
    def __setitem__(self, key, value):
        print ('__setitem__',key,value)
 
    def __delitem__(self, key):
        print ('__delitem__',key)
 
 
obj = Foo()
 
result = obj['k1']      # 自动触发执行 __getitem__
obj['k2'] = 'maple'   # 自动触发执行 __setitem__
del obj['k1']           # 自动触发执行 __delitem__

输出结果也是简单并且可预见的:

__getitem__ k1
__setitem__ k2 maple

__delitem__ k1
可序列化的类

在这里插入图片描述这个地方目前涉及比较少,以后遇到再补充

可以再with语块中使用的类

在这里插入图片描述


# excerpt from io.py: 
def _checkClosed(self, msg=None):  
    '''Internal: raise an ValueError if file is closed '''  
     if self.closed:  
          raise ValueError('I/O operation on closed file.' if msg is None else msg) 
   
def __enter__(self):  
   '''Context management protocol. Returns self.'''  
    self._checkClosed()  
    return self  

def __exit__(self, *args):  
   '''Context management protocol. Calls close()'''  
    self.close()  
类的创建中常用的方法

在这里插入图片描述

说在后面的话

在文章的整理过程中,有一些参考,下面将感觉写的不错的博客附录在下面,方便大家查阅

博客一

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值