详解Python类中的特殊方法(前后都有双下划线的方法)-__len__、__str__、__getitem__、__iter__、__getattr__、__call__等

其他关于Python的总结文章请访问://www.greatytc.com/nb/47435944

详解Python类中的特殊方法(前后都有双下划线的方法)-lenstrgetitemitergetattrcall

类中定义的一些特殊方法,也就是方法名称前后都有双下划线标识的方法,都具有特殊的意义,重写这些方法可以帮助类更好地发挥功能,这里主要介绍几种常用的、重要的方法。

我们以一个偶数类(Even)为例,其中会创建一个N个偶数的列表,同时创建一个该类的实例even

class Even:
    def __init__(self, N):
        self.N = N
        self.even_list = [2 * x for x in range(N)]

even = Even(10)

len方法

__len__方法在当 len 函数作用于该类的时候被调用,比如我们定义Even类的长度就是偶数列表的个数:

def __len__(self):
    return self.N

此时调用len函数作用于Even类的实例上就会调用 __len__ 方法:

print(len(even)) # 20

如果没有定义 __len__ 方法,当len函数作用于类的实例上时就会报错:

TypeError: object of type 'Even' has no len()

str方法

__str__方法在当 print 函数作用于该类的时候被调用,比如我们继续编写Even类,加入__str__方法:

def __str__(self):
    return "An Even Numbers List with {} Items:\n{}".format(self.N, self.even_list)

此时调用 print(even) 就可以得到如下的结果:

An Even Numbers List with 10 Items:
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

repr方法

__repr__方法是在直接(比如解释器中)直接调用类或者实例时被触发的,它和__str__很像,唯一不同的是没有print操作,比如这样调用时会触发 __repr__(Python解释器中):

>>> even

注意上边跟 >>> print(even) 的区别)

一般来说__repr__方法和__str__方法输出的内容相同,所以通常可以使用简单的方式来定义__repr__方法:

__repr__ = __str__

getitem方法

__getitem__方法在对类进行迭代时触发,而且必须传入一个整数作为参数:

def __getitem__(self, n):
    return self.even_list[n]

这样就可以使用for循环进行对Even类的遍历了,或者使用 even[i] 这样对Even类进行索引,甚至是使用切片:

for i in range(len(even)):
    print(even[i], end=' ')

print()

for e in even:
    print(e, end=' ')

print()

print(even[2:8])
print(even[8:2:-1])

可以得到输出结果:

0 2 4 6 8 10 12 14 16 18 
0 2 4 6 8 10 12 14 16 18 
[4, 6, 8, 10, 12, 14]
[16, 14, 12, 10, 8, 6]

setitem方法

__setitem__方法是和__getitem__方法对应的,用来更改某下标所对应处的值,必须传入两个参数,一个key用于指定要修改的下标,一个value用于告知要修改的值:

def __setitem__(self, key, value):
    self.even_list[key] = value

这样就可以修改类的某个索引处的值了:

print(even[0])  # 0
even[0] = 100
print(even[0])  # 100

delitem方法

__delitem__方法实现了del作用于类时的逻辑:

def __delitem__(self, key):
    del self.even_list[key]
    self.N = len(self.even_list)

这样使用 del 作用与类就可以得到相应的结果:

print(even)
del even[0:5]
print(even)

得到的结果:

An Even Numbers List with 10 Items:
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
An Even Numbers List with 5 Items:
[10, 12, 14, 16, 18]

iter方法和next方法

__iter__方法返回一个可迭代对象,供 for ... in ... 循环对类进行迭代,同时配合__next__方法在对类使用 next 函数时调用获取下一个迭代值,并且 __next__方法中可以实现迭代结束后给出 StopIteration 错误的逻辑。

getattr方法

__getattr__方法在类的实例试图调用一个不存在的属性时触发,可以对应地返回一个属性值或者函数,也可以完成对于一些属性抛出错误的逻辑:

def __getattr__(self, item):
    if item == 'length':
        return self.N
    if item == 'get_length':
        return lambda: self.N
    raise AttributeError("Even doesn't have the attribute {}".format(item))

此时如果这样使用类,就会得到相应的结果:

print(even.length)  # 10
print(even.get_length())  # 10
print(even.some_other_attribute)  # AttributeError: Even doesn't have the attribute some_other_attribute

注意该方法只会处理调用不存在的属性的情况,如果属性存在则不会到__getattr__方法中寻找

call方法

__call__方法在类的实例被调用的时候触发,还可以接收参数,我们可以使用callable函数来判断一个类的实例是不是可调用的,callable一个没有定义__call__方法的类的实例会返回False,反之返回True

def __call__(self):
    print("Hello, you called me: an even numbers list with {} items".format(self.N))

此时调用该类的实例:

even()
print(callable(even))

就会得到相应的结果:

Hello, you called me: an even numbers list with 10 items
True

上述内容的完整类的代码

class Even:
    def __init__(self, N):
        self.N = N
        self.even_list = [2 * x for x in range(N)]

    def __len__(self):
        return self.N

    def __str__(self):
        return "An Even Numbers List with {} Items:\n{}".format(self.N, self.even_list)

    __repr__ = __str__

    def __getitem__(self, n):
        return self.even_list[n]

    def __setitem__(self, key, value):
        self.even_list[key] = value

    def __delitem__(self, key):
        del self.even_list[key]
        self.N = len(self.even_list)

    def __getattr__(self, item):
        if item == 'length':
            return self.N
        if item == 'get_length':
            return lambda: self.N
        raise AttributeError("Even doesn't have the attribute {}".format(item))

    def __call__(self):
        print("Hello, you called me: an even numbers list with {} items".format(self.N))
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,718评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,683评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,207评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,755评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,862评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,050评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,136评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,882评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,330评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,651评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,789评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,477评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,135评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,864评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,099评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,598评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,697评论 2 351

推荐阅读更多精彩内容