本章介绍一下python类中的一些内置方法。内置的方法有很多,有些内置方法在object类中已经定义,子类可以拿来直接使用,也可以重写,但是有些内置方法object类中没有,比如 __len__ 方法(len()方法会调用 对应类中的 __len__ 方法),需要根据需求来进行定义~

__str__和__repr__

对象的字符串显示 会调用__str__ 或 __repr__ 方法,__str__ 和 __repr__ 方法在object中已经定义,默认都是输出对象在内存中的地址,有时候根据需求需要重写这两个方法~

class Person: def __init__(self, name, age, city): self.name = name self.age = age self.city = city def __repr__(self): return '(%s,%s)' % (self.name, self.city) def __str__(self): return '(%s,%s)' % (self.name, self.age)p = Person('Kitty', 19, 'HZ')print(p) # (Kitty,19),调用 __str__print(str(p)) # (Kitty,19),调用 __str__print(repr(p)) # (Kitty,HZ),调用 __repr__print('%s' % p) # (Kitty,19),调用 __str__print('%r' % p) # (Kitty,HZ),调用 __repr__

在交互式解释其中,直接输出对象,会调用 __repr__ 方法

>>> p = Person('Kitty', 19, 'HZ')>>> p(Kitty,HZ)

Tip:

str函数,print函数,%s 会调用 __str__ 方法;repr函数,交互式解释器,%r 会调用 __repr__ 方法;若是 仅重写了__repr__ 方法,那么 str函数,print函数,%s 就会用__repr__ 方法来代替,但是反过来 __str__ 方法 不会代替 __repr__ 方法~__format__

用于将对象格式化输出

class Person: def __init__(self, name, age, city): self.name = name self.age = age self.city = city def __format__(self, format_spec): fmt = { '-': '{obj.name}-{obj.age}-{obj.city}', '/': '{obj.name}/{obj.age}/{obj.city}' }[format_spec] return fmt.format(obj = self)p = Person('Kitty', 19, 'HZ')# format 函数会调用对象的绑定方法 __format__print(format(p, '-')) print(format(p, '/'))# 输出结果:Kitty-19-HZKitty/19/HZ__del__

析构方法,当对象在内存中被释放时,会自动触发执行。但是此方法一般无须定义,因为Python解释器会来完成内存的分配和释放工作,所以,析构方法的调用是由解释器在进行垃圾回收时自动触发执行的。

class Person: def __init__(self, name, age, city): self.name = name self.age = age self.city = city def __del__(self): print('delete...')p = Person('Kitty', 19, 'HZ')del p# 输出结果:delete...item系列

class Person: def __init__(self, name, age): self.name = name self.age = age def __getitem__(self, item): return self.__dict__[item] def __setitem__(self, key, value): self.__dict__[key] = value def __delitem__(self, item): self.__dict__.pop(item) def __delattr__(self, key): self.__dict__.pop(key)p = Person('Kitty', 19)print(p['name']) # Kitty,调用 __getitem__ 方法p['age'] = 18print(p['age']) # 18p['city'] = 'HZ' print(p.__dict__) # 调用 __setitem__ 方法# {'name': 'Kitty', 'age': 18, 'city': 'HZ'}del p['city'] # 调用 __delitem__ 方法print(p.__dict__) # {'name': 'Kitty', 'age': 18}del p.age # 调用 __delattr__ 方法print(p.__dict__)# {'name': 'Kitty'}__len__

len(obj) 会调用obj的 __len__ 方法

class Person: def __init__(self, name, age): self.name = name self.age = age def __len__(self): return len(self.__dict__)p = Person('Kitty', 19)print(len(p)) # 2__call__

对象后面加括号,就会触发 __call__ 方法的执行,即调用方式:对象() 或者 类()()

class Person: def __init__(self, name, age): self.__name = name self.__age = age def __call__(self): print('hello' + ' ' + self.__name)p = Person('Kitty', 19)() # hello Kitty# p()__new__

在类的是实例化过程中,第一个被调用的是 __new__方法。在对象的初始化之前首先要创建对象,__new__方法正是用来创建这个对象~
 ;
类的实例化过程也可以通过如下语句来实现:

>>> p = object.__new__(Person) >>> Person.__init__(p, 'Kitty', 18)>>> p.name'Kitty'

在Person类中重写 __new__方法:

class Person: country = "China" def __new__(cls, name, age): print('__new__ called') return super(Person, cls).__new__(cls) def __init__(self, name, age): self.name = name self.age = age def speak(self, word): print(word)p = Person('Kitty', 18) # 实例化对象print('name : %s' % p.name)# 输出结果:__new__ calledname : Kitty

可以看到 __new__ 方法 是先于 __init__ 方法被调用的~
类的实例化过程大致步骤如下:
1、p = Person('Kitty', 18) ,会调用 Person类的 __new__ 方法,并传递 name 和 age 参数
2、__new__ 方法 会创建一个 Person类的对象并返回
3、最后利用这个对象调用类的 __init__ 方法 完成初始化,__init__ 方法的第一个参数是self,对象在调用 __init__ 方法时会将自己当做参数传递给 这个self。

单例模式

重写 __new__ 来实现单例模式~

class Person: def __new__(cls, *args, **kwargs): if not hasattr(cls, '_instance'): cls._instance = super(Person, cls).__new__(cls, *args, **kwargs) return cls._instancep1 = Person()p2 = Person()print(p1) # <__main__.Person object at 0x108eb6438>print(p2) # <__main__.Person object at 0x108eb6438>

对象的内存地址相同,则表示为同一个对象~

__hash__和__eq__

在判断两个对象是否一致时,往往会用到这两个函数~

__hash__

class Person: def __init__(self, name, age): self.__name = name self.__age = age def __hash__(self): return hash(str(self.__name))p = Person('Kitty', '19')print(hash(p))__eq__

class Person: def __init__(self, name, age): self.name = name self.age = age def __eq__(self, obj): if self.name == obj.name and self.age == obj.age: return Truep1 = Person('Kitty', '19')p2 = Person('Kitty', '19')print(p1 == p2) # True

 
集合(set)是一个无序不重复元素的序列。集合中只能存放不可变对象(可hash的)。在向 set 集合中添加对象时,会通过调用对象的 __hash__ 和 __eq__ 这两个方法来判断集合中是否已经存在一样的对象~

class Person: def __init__(self, name, age): self.name = name self.age = age def __hash__(self): print('调用了__hash__方法') return hash(self.name) def __eq__(self, obj): print('调用了__eq__方法') return self.name == obj.name and self.age == obj.age def __str__(self): return '(' + self.name + ', ' + self.age + ')'p1 = Person('Tom', '19') # 调用了__hash__方法p2 = Person('Jeff', '20') # 调用了__hash__方法my_set = {p1, p2}for i in my_set: print(i) # (Jeff, 20), (Tom, 19)

在向 set 集合中添加对象时,会首先调用对象的 __hash__ 若是 返回的 hash值不一样,则认为不是重复的对象,进行添加~

p1 = Person('Tom', '19') # 调用了__hash__方法p2 = Person('Tom', '20') # 调用了__hash__方法,调用了__eq__方法my_set = {p1, p2}for i in my_set: # (Tom, 20), (Tom, 19) print(i)

若是返回的hash值有重复的,则会接着调用对象 __eq__ 方法,若 __eq__ 方法的返回值为False,则认为不是重复对象,进行添加,若返回为True,则不进行添加~
 
所以总结一下:set结合的去重是通过调用对象的 __hash__ 和 __eq__ 这两个方法共同实现的
1、首先调用对象的 __hash__方法,返回的hash值不存在重复,则直接添加该对象;
2、当返回的hash值存在重复,接着再调用 __eq__ 方法,返回为False,添加对象,返回为True,不进行添加~

.................^_^