Quando definimos uma classe, o Python nos fornece automaticamente alguns métodos, a maioria dos quais é herdada da classe de objeto ou classe de tipo. Podemos substituir esses métodos para implementar operações específicas.
20.1 Tornando os objetos mais fáceis de ler quando impressos
Imprimir o objeto com print() exibirá __str__()
o conteúdo da função. Ao executar a instância diretamente, o objeto impresso exibirá __repr__()
o conteúdo da função. Portanto, através da implementação __str__()
e __repr__()
desses dois métodos, a impressão do objeto pode estar mais de acordo com a leitura humana. por exemplo
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self): # print()时调用
return 'Student object (name=%s, age=%d)' % (self.name, self.age)
__repr__ = __str__
print(Student("Jim", 20)) # 打印__str__函数的内容,Student object (name=Jim, age=20)
20.2 Fazendo objetos de classe suportarem iteração
Sabemos que todos os tipos de contêiner suportam ... in ... para loops iterativos. A instrução for realmente faz duas coisas. A primeira coisa é obter um iterador, ou seja, a __iter__()
função é chamada. A segunda coisa é o processo de loop, chamando __next__()
funções em um loop. Na verdade, uma classe personalizada, desde que esses dois métodos sejam implementados, o objeto da classe também pode ser iterado pelo loop for.
Por exemplo, a sequência de Fibonacci, implementada com uma classe:
class Fib:
def __init__(self):
self.a, self.b = 0, 1 # 初始化两个计数器a,b
def __iter__(self):
return self # 返回一个迭代对象,实例本身就是迭代对象,故返回自己
def __next__(self): # for循环时就是调用这个方法获取下一个值
self.a, self.b = self.b, self.a + self.b # 计算下一个值
if self.a > 10000: # 退出循环的条件
raise StopIteration()
return self.a # 返回下一个值
for i in Fib():
if i > 20:
break
print(i)
20.3 Tornando objetos chamáveis
As funções, funções internas e classes que normalmente definimos são todos objetos que podem ser chamados. A sensação intuitiva é que podemos adicionar um par de parênteses depois deles, mas se pudermos aplicar um par de parênteses () a um objeto, esse objeto Ele pode ser chamado de objeto que pode ser chamado, e a função callable() pode ser usada para julgar se o objeto é um objeto que pode ser chamado.
callable(0) # 函数返回 False
def add(a, b):
return a + b
callable(add) # 函数返回 True
Se o __call__
método , o objeto de instância também se tornará um objeto que pode ser chamado. Ou seja, a instância pode ser seguida por parênteses e o resultado da chamada é __call__
o conteúdo do método. Veja um exemplo:
class Student:
def __init__(self, name):
self.name = name
def __call__(self):
print('My name is %s.' % self.name)
s = Student('Michael')
s() # 实例变成了Callable的了,输出My name is Michael
Qual é a utilidade prática disso? Vejamos um exemplo, usando __getattr__
e __call__
, para obter uma chamada dinâmica, gere dinamicamente uma string de URL.
class Chain:
def __init__(self, path=''):
self._path = path
def __getattr__(self, path):
"""调用不存在的属性时,会执行这个方法,方法效果是将path用/拼接起来,返回一个实例"""
return Chain('%s/%s' % (self._path, path))
def __call__(self, path):
"""当实例被调用时,会执行这个方法,方法效果是实例里面的参数也用/拼接起来,返回一个实例"""
return Chain('%s/%s' % (self._path, path))
def __str__(self):
return self._path
__repr__ = __str__
if __name__ == '__main__':
print(Chain().status.user.timeline.list) # 每一个"."就是一次调用__getattr__方法
print(Chain().users('michael').repos) # Chain().users根据__getattr__的作用会返回一个实例,加上()后回到用__call__方法
20.4 Criar manualmente um objeto de instância
__new__
O método é responsável por criar um objeto instância.O método é chamado automaticamente pelo interpretador Python quando o objeto é criado.É um método de classe. __new__
Depois que o método retornar uma instância, o interpretador Python continuará a chamar automaticamente o __init__
método para inicializar a instância. Se __new__
o método não retornar um valor ou não retornar uma instância, o interpretador Python não chamará automaticamente o __init__
método. __new__
O método é principalmente ao herdar uma classe (como int, str, tuple), para fornecer aos programadores a oportunidade de personalizar o processo de instanciação dessas classes. Há também a realização de metaclasse de metaclasse customizada.
__new__
Uma aplicação típica é criar um padrão singleton.
O princípio do modo singleton é adicionar um sinalizador de bit de determinação singleton ao atributo de classe e usar esse sinalizador para determinar se a classe foi instanciada.Se a classe foi instanciada, o objeto instanciado é retornado.
class Singleton:
# 重写父类的__new__方法,__new__是个类方法,第一个参数是cls,必须有返回值
def __new__(cls, *args, **kwargs): # 自定义类实例化对象的过程
# __new__生成的对象放入cls的_instance属性中, 如果cls不存在_instance
if not hasattr(cls, "_instance"):
cls._instance = super().__new__(cls, *args, **kwargs) # 调用父类__new__创建实例
# 如果cls存在_instance,直接返回,不要生成新的对象
return cls._instance
class MyClass(Singleton):
pass
s1 = MyClass()
s2 = MyClass()
s3 = MyClass("s3")
s4 = MyClass("s4")
print(id(s1), id(s2), id(s3), id(s4))
As classes que herdam de Singleton __new__
serão singletons, a menos que sejam substituídas.
20.5 Personalizando a lógica de comparação de dois objetos
O método é acionado quando é determinado se dois objetos são iguais __eq__
. Neste método você pode definir regras para igualdade de dois objetos. Existem também métodos __lt__
e __gt__
, que definem as regras para comparar o tamanho de dois objetos. Por exemplo:
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other): # 定义内置方法,自定义两个对象相等的逻辑
return self.__dict__ == other.__dict__ # 两对象空间的属性值都相等,两个对象被认为相等。
def __lt__(self, other):
return self.age < other.age
if __name__ == '__main__':
s1 = Student("李四", 20)
s2 = Student("李四", 20)
print(s2 == s1) # True
print(s2 is s1) # False
s3 = Student("王五", 19)
print(s1 > s3) # True