python解析器在碰到特殊的句法时,会使用特殊方法(魔术方法)来激活一些基本的对象操作,这些特殊的方法指的是形如__func__的方法。
比如my_collection[key]这种特殊的句法就会调用my_collection.__getitem__(key)
通过一个纸牌例子来学习:
# -*- coding:utf-8 -*-
import collections
from random import choice
# 用来构建一些只有少数属性没有方法的类
Card = collections.namedtuple('Card', ['rank', 'suit'])
class FrenchDeck:
ranks = [str(n) for n in range(2,11) + list('JQKA')]
suits = 'spades diamonds clubs hearts'.split()
def __init__(self):
self._cards = [Card(rank, suit) for suit in self.suits for rank in self.ranks]
def __len__(self):
return len(self._cards)
def __getitem__(self, position):
return self._cards[position]
if __name__ == '__main__':
deck = FrenchDeck()
#len()会调用类的__len__方法
print(len(deck))
#deck[]会调用__getitem__方法
print(deck[0])
print(deck[-1])
print(choice(deck))
print(deck[:3])
print(deck[12::13])
结果:
52
Card(rank='2', suit='spades')
Card(rank='A', suit='hearts')
Card(rank='2', suit='hearts')
[Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')]
[Card(rank='A', suit='spades'), Card(rank='A', suit='diamonds'), Card(rank='A', suit='clubs'), Card(rank='A', suit='hearts')]
特殊方法的存在是为了被python解析器调用的,而不是给自己调用的,一般我们通过内置函数使用特殊方法,并且不要随意添加特殊方法。比如说:不会这样用:my_object.__len__(),而是这样用:len(my_object)。如果是python内置的类型,情况会有些变化,比如列表,字符串,这样python会”抄近路“,直接读它们的ob_size属性,因为这样会运行的快一点。很多时候特殊方法的调用是隐式的,比如for i in x:这个语句,背后其实用的是iter(x),这个函数的背后是x.__iter__()方法
常见的一些特殊方法:
数学相关
- abs(a) : 求取绝对值。abs(-1)
- max(list) : 求取list最大值。max([1,2,3])
- min(list) : 求取list最小值。min([1,2,3])
- sum(list) : 求取list元素的和。 sum([1,2,3]) >>> 6
- sorted(list) : 排序,返回排序后的list。
- len(list) : list长度,len([1,2,3])
- divmod(a,b): 获取商和余数。 divmod(5,2) >>> (2,1)
- pow(a,b) : 获取乘方数。pow(2,3) >>> 8
- round(a,b) : 获取指定位数的小数。a代表浮点数,b代表要保留的位数。round(3.1415926,2) >>> 3.14
- range(a[,b]) : 生成一个a到b的数组,左闭右开。 range(1,10) >>> [1,2,3,4,5,6,7,8,9]
类型转换
- int(str) : 转换为int型。int('1') >>> 1
- float(int/str) : 将int型或字符型转换为浮点型。float('1') >>> 1.0
- str(int) : 转换为字符型。str(1) >>> '1'
- bool(int) : 转换为布尔类型。 str(0) >>> False str(None) >>> False
- bytes(str,code) : 接收一个字符串,与所要编码的格式,返回一个字节流类型。bytes('abc', 'utf-8') >>> b'abc' bytes(u'爬虫', 'utf-8') >>> b'\xe7\x88\xac\xe8\x99\xab'
- list(iterable) : 转换为list。 list((1,2,3)) >>> [1,2,3]
- iter(iterable): 返回一个可迭代的对象。 iter([1,2,3]) >>> <list_iterator object at 0x0000000003813B00>
- dict(iterable) : 转换为dict。 dict([('a', 1), ('b', 2), ('c', 3)]) >>> {'a':1, 'b':2, 'c':3}
- enumerate(iterable) : 返回一个枚举对象。
- tuple(iterable) : 转换为tuple。 tuple([1,2,3]) >>>(1,2,3)
- set(iterable) : 转换为set。 set([1,4,2,4,3,5]) >>> {1,2,3,4,5} set({1:'a',2:'b',3:'c'}) >>> {1,2,3}
- hex(int) : 转换为16进制。hex(1024) >>> '0x400'
- oct(int) : 转换为8进制。 oct(1024) >>> '0o2000'
- bin(int) : 转换为2进制。 bin(1024) >>> '0b10000000000'
- chr(int) : 转换数字为相应ASCI码字符。 chr(65) >>> 'A'
- ord(str) : 转换ASCI字符为相应的数字。 ord('A') >>> 65
相关操作
- eval() : 执行一个表达式,或字符串作为运算。 eval('1+1') >>> 2
- exec() : 执行python语句。 exec('print("Python")') >>> Python
- filter(func, iterable) : 通过判断函数fun,筛选符合条件的元素。 filter(lambda x: x>3, [1,2,3,4,5,6]) >>> <filter object at 0x0000000003813828>
- map(func, *iterable) : 将func用于每个iterable对象。 map(lambda a,b: a+b, [1,2,3,4], [5,6,7]) >>> [6,8,10]
- zip(*iterable) : 将iterable分组合并。返回一个zip对象。 list(zip([1,2,3],[4,5,6])) >>> [(1, 4), (2, 5), (3, 6)]
- type():返回一个对象的类型。
- id(): 返回一个对象的唯一标识值。
- hash(object):返回一个对象的hash值,具有相同值的object具有相同的hash值。 hash('python') >>> 7070808359261009780
- help():调用系统内置的帮助系统。
- isinstance():判断一个对象是否为该类的一个实例。
- issubclass():判断一个类是否为另一个类的子类。
- globals() : 返回当前全局变量的字典。
- next(iterator[, default]) : 接收一个迭代器,返回迭代器中的数值,如果设置了default,则当迭代器中的元素遍历后,输出default内容。
- reversed(sequence) : 生成一个反转序列的迭代器。 reversed('abc') >>> ['c','b','a']
__repr__和__str__区别:
__repr__ 目的是为了表示清楚,是为开发者准备的。
__str__ 目的是可读性好,是为使用者准备的。
我的理解是 __repr__ 应该尽可能的表示出一个对象来源的类以及继承关系,方便程序员们了解这个对象。而 __str__ 就简单的表示对象。 print一个对象调用的是__str__方法