【重要 Python(17)魔法函数 \_\_xx\_\_】

什么是魔法函数?

Python的一种高级语法

一般用于在类中自定义函数(函数名格式一般为__xx__),并且绑定到类的特殊方法中。

比如在类A中自定义__str__()函数,则在调用str(A())时,会自动调用__str__()函数,并返回相应的结果。

  • Python中以双下划线(xx)开始和结束的函数(不可自己定义)为魔法函数。

  • 调用类实例化的对象的方法时自动调用魔法函数。

  • 在自己定义的类中,可以实现之前的内置函数。

魔法函数的应用

魔法函数可以为你写的类增加一些额外功能,

举个简单的例子,我们定义一个“人”的类People,当中有属性姓名name、年龄age。让你需要利用sorted函数对一个People的数组进行排序,排序规则是按照name和age同时排序,即name不同时比较name,相同时比较age。由于People类本身不具有比较功能,所以需要自定义,你可以这么定义People类:

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

    def __str__(self):
        return self.name + ":" + str(self.age)

    def __lt__(self, other):
        return self.name < other.name if self.name != other.name else self.age < other.age


if __name__=="__main__":

    print("\t".join([str(item) for item in sorted([People("abc", 18),
        People("abe", 19), People("abe", 12), People("abc", 17)])]))
        

>>>
abc:17  abc:18  abe:12  abe:19

上个例子中的__lt__函数即less than函数,即当比较两个People实例时自动调用。

非数学运算——魔法函数

一、字符串表示

1. __repr__函数

函数__str__()用于将值转化为适于人阅读的形式,而__repr__()转化为供解释器读取的形式,某对象没有适于人阅读的解释形式的话,__str__() 会返回与__repr__(),所以print展示的都是__str__()的格式。

class CLanguage:
    pass
clangs = CLanguage()
print(clangs)

>>>
<__main__.CLanguage object at 0x000001A7275221D0>

默认情况下,我们得到的信息只会是“类名+object at+内存地址”,对我们了解该实例化对象帮助不大.

有没有可能自定义输出实例化对象时的信息呢?答案是肯定,通过重写类的 __repr__()方法即可。事实上,当我们输出某个实例化对象时,其调用的就是该对象的__repr__()方法,输出的是该方法的返回值。

class CLanguage:
    def __init__(self):
        self.name = "C语言中文网"
        self.add = "http://c.biancheng.net"
    def __repr__(self):
        return "CLanguage[name="+ self.name +",add=" + self.add +"]"
clangs = CLanguage()
clangs


>>>
CLanguage[name=C语言中文网,add=http://c.biancheng.net]

通过 自定义__repr__()方法实现重写

2. __str__函数

未修改__str__() 前,示例:

class Cat:
    """定义一个猫类"""
 
    def __init__(self, new_name= "汤姆", new_age= 20):
        """在创建完对象之后 会自动调用, 它完成对象的初始化的功能"""
        self.name = new_name
        self.age = new_age  
 
tom = Cat("Tom",30)
print(tom)


>>>
<__main__.Cat object at 0x0000012F7F80D040>

修改后:

class Cat:
    """定义一个猫类"""
 
    def __init__(self, new_name= "汤姆", new_age= 20):
        """在创建完对象之后 会自动调用, 它完成对象的初始化的功能"""
        self.name = new_name
        self.age = new_age  # 它是一个对象中的属性,在对象中存储,即只要这个对象还存在,那么这个变量就可以使用
        
    def __str__(self):
        """返回一个对象的描述信息"""
        # print(num)
        return "名字是:%s , 年龄是:%d" % (self.name, self.age)

# 创建了一个对象
tom = Cat("汤姆", 30)
print(tom)

>>>
名字是:汤姆 , 年龄是:30

__repr__和 __str__的区别
class Test(object):
    def __init__(self, value='hello, world!'):
        self.data = value

>>> t = Test()
>>> t
<__main__.Test at 0x7fa91c307190>
>>> print(t)
<__main__.Test object at 0x7fa91c307190>

# 看到了么?上面打印类对象并不是很友好,显示的是对象的内存地址
# 下面我们重构下该类的__repr__以及__str__,看看它们俩有啥区别

# 重构__repr__
class TestRepr(Test):
    def __repr__(self):
        return 'TestRepr(%s)' % self.data

>>> tr = TestRepr()
>>> tr               直接终端显示,不print就是面向程序员
TestRepr(hello, world!)
>>> print(tr)         print是面向程序员
TestRepr(hello, world!)

# 重构__repr__方法后,不管直接输出对象还是通过print打印的信息都按我们__repr__方法中定义的格式进行显示了

# 重构__str__
calss TestStr(Test):
    def __str__(self):
        return '[Value: %s]' % self.data

>>> ts = TestStr()
>>> ts
<__main__.TestStr at 0x7fa91c314e50>
>>> print(ts)
[Value: hello, world!]

# 你会发现,直接输出对象ts时并没有按我们__str__方法中定义的格式进行输出,而用print输出的信息却改变了

二、集合序列相关

3. __len__ 函数

在Python中,如果你调用len()函数试图获取一个对象的长度,实际上,在len()函数内部,它自动去调用该对象的__len__()方法。

class Students():
    def __init__(self, *args):
        self.names = args
    def __len__(self):
        return len(self.names)

ss = Students('Bob', 'Alice', 'Tim')
print(len(ss))

>>>
3
4.__getitem__ 函数

Python的特殊方法__getitem _() 主要作用是可以让对象实现迭代功能。我们通过一个实例来说明。

定义一个Sentence类,通过索引提取单词。

import re
RE_WORD = re.compile(r'\w+')
class Sentence:
    def __init__(self, text):
        self.text = text
        self.words = RE_WORD.findall(text)  # re.findall函数返回一个字符串列表,里面的元素是正则表达式的全部非重叠匹配
    def __getitem__(self, index):
        return self.words[index]  

s = Sentence('The time has come')
s[0]
s[1]
>>>
The
time

通过测试发现,示例 s 可以正常迭代。但是没有定义getitem() 测试则会报错,TypeError: '***' object is not subscriptable

列可以迭代:

我们都知道序列是可以迭代,下面具体说明原因。

解释器需要迭代对象x时, 会自动调用iter(x)方法。内置的 iter(x) 方法有以下作用:

  • 检查对象是否实现了__iter__ 方法,如果实现了就调用它(也就是我们偶尔用到的特殊方法重载),获取一个迭代器。

  • 如果没有实现iter()方法, 但是实现了 __getitem__方法,Python会创建一个迭代器,尝试按顺序(从索引0开始,可以看到我们刚才是通过s[0]取值)获取元素。

  • 如果尝试失败,Python抛出TypeError异常,通常会提示TypeError: '***' object is not ``subscriptable

任何Python序列都可迭代的原因是,他们都实现了__getitem__方法。其实,标准的序列也都实现了__iter__方法。

注意: 从python3.4 开始,检查对象x能否迭代,最准确的方法是: 调用iter(x)方法,如果不可迭代,在处理TypeError异常。这比使用isinstance(x,abc.Iterable)更准确,因为iter()方法会考虑到遗留的__getitem__()方法,而abc.Iterable类则不考虑。

凡是在类中定义了这个__getitem__ 方法,那么它的实例对象(假定为p),可以像这样p[key] 取值,当实例对象做p[key] 运算时,会调用类中的方法__getitem__

一般如果想使用索引访问元素时,就可以在类中定义这个方法(getitem(self, key) ),当实例对象通过[] 运算符取值时,会调用它的方法__getitem__

class DataBase:
    '''Python 3 中的类'''

    def __init__(self, id, address):
        '''初始化方法'''
        self.id = id
        self.address = address
        self.d = {self.id: 1,
                  self.address: "192.168.10.10",
                  }

    def __getitem__(self, key):
        # return self.__dict__.get(key, "100")
        return self.d.get(key, "default")


data = DataBase(1, "192.168.2.11")
print(data["hi"])
print(data[data.id])

>>>
default
1

or

class DataBase:
    '''Python 3 中的类'''

    def __init__(self, id, address):
        '''初始化方法'''
        self.id = id
        self.address = address

    def __getitem__(self, key):
        return self.__dict__.get(key, "100")


data = DataBase(1, "192.168.2.11")
print(data["hi"])
print(data["id"])

>>>
100
1

还可以用在对象的迭代上

class STgetitem:

    def __init__(self, text):
        self.text = text

    def __getitem__(self, index):
        result = self.text[index].upper()
        return result


p = STgetitem("Python")
print(p[0])
print("------------------------")
for char in p:
    print(char)

>>>
P
------------------------
P
Y
T
H
O
N

5.__setitem__ 函数

setitem(self,key,value):该方法应该按一定的方式存储和key相关的value。在设置类实例属性时自动调用的。

# -*- coding:utf-8 -*-
 
class A:
    def __init__(self):
        self['B']='BB'
        self['D']='DD'
        
    def __setitem__(self,name,value):
 
        print "__setitem__:Set %s Value %s" %(name,value)
        
        
if __name__=='__main__':
    X=A()

>>>
__setitem__:Set B Value BB
__setitem__:Set D Value DD

6.__delitem__ 函数

delitem(self,key):

这个方法在对对象的组成部分使用__del__语句的时候被调用,应删除与key相关联的值。同样,仅当对象可变的时候,才需要实现这个方法。

class Tag:
    def __init__(self):
        self.change={'python':'This is python',
                     'php':'PHP is a good language'}
 
    def __getitem__(self, item):
        print('调用getitem')
        return self.change[item]
 
    def __setitem__(self, key, value):
        print('调用setitem')
        self.change[key]=value
 
    def __delitem__(self, key):
        print('调用delitem')
        del self.change[key]
 
a=Tag()
print(a['php'])
del a['php']
print(a.change)

>>>
调用getitem
PHP is a good language
调用delitem
{'python': 'This is python'}

7.__contains 函数

在Class里添加__contains__(self,x)函数,可判断我们输入的数据是否在Class里.参数x就是我们传入的数据.

如下代码:

class Graph():
    def __init__(self):
        self.items = {'a':1,'b':2,'c':3}
    def __contains__(self,x): # 判断一个定点是否包含在里面
        return x in self.items

a = Graph()
print('a' in a) # 返回True
print('d' in a) # 返回False

>> True
>> False
class Graph():
    def __init__(self):
        self.items = {'a':1,'b':2,'c':3}

    def __str__(self):
        return '打印我干嘛'
    
    def __contains__(self,x): # x参数接受的就是我们手动传递的数据
        if x<10 and x>0:
            return True
        return False

print(9 in Graph())
print(5 in Graph())
print(51 in Graph())

>> True
>> True
>> False
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Eason DayDayUp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值