__str__

改变对象的字符串显示。可以理解为使用print函数打印一个对象时,会自动调用对象的__str__方法

class Student:
def __init__(self, name, age):
self.name = name
self.age = age # 定义对象的字符串表示
def __str__(self):
return self.name s1 = Student('张三', 24)
print(s1) # 会调用s1的__str__方法

__repr__

在python解释器环境下,会默认显示对象的repr表示。

>>> class Student:
... def __init__(self, name, age):
... self.name = name
... self.age = age
... def __repr__(self):
... return self.name
...
>>> s1 = Student('张三', 24)
>>> s1
张三

总结:

str函数或者print函数调用的是obj.__str__()
repr函数或者交互式解释器调用的是obj.__repr__()

注意:
如果__str__没有被定义,那么就会使用__repr__来代替输出。
__str__和__repr__方法的返回值都必须是字符串。

__format__

class Student:
def __init__(self, name, age):
self.name = name
self.age = age __format_dict = {
'n-a': '名字是:{obj.name}-年龄是:{obj.age}', # 名字是:lqz-年龄是:18
'n:a': '名字是:{obj.name}:年龄是:{obj.age}', # 名字是:lqz:年龄是:18
'n/a': '名字是:{obj.name}/年龄是:{obj.age}', # 名字是:/年龄是:18
} def __format__(self, format_spec):
if not format_spec or format_spec not in self.__format_dict:
format_spec = 'n-a'
fmt = self.__format_dict[format_spec]
print(fmt) #{obj.name}:{obj.age}
return fmt.format(obj=self) s1 = Student('lqz', 24)
ret = format(s1, 'n/a')
print(ret) # lqz/24

__del__

析构方法,当对象在内存中被释放时,自动触发执行。

注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

class A:
def __del__(self):
print('删除了...') a = A()
print(a) # <__main__.A object at 0x10164fb00>
del a # 删除了...
print(a) # NameError: name 'a' is not defined

__dict__和__slots__

Python中的类,都会从object里继承一个__dict__属性,这个属性中存放着类的属性和方法对应的键值对。一个类实例化之后,这个类的实例也具有这么一个__dict__属性。但是二者并不相同。

class A:
some = 1 def __init__(self, num):
self.num = num a = A(10)
print(a.__dict__) # {'num': 10}
a.age = 10
print(a.__dict__) # {'num': 10, 'age': 10}

从上面的例子可以看出来,实例只保存实例的属性和方法,类的属性和方法它是不保存的。正是由于类和实例有__dict__属性,所以类和实例可以在运行过程动态添加属性和方法。

但是由于每实例化一个类都要分配一个__dict__变量,容易浪费内存。因此在Python中有一个内置的__slots__属性。当一个类设置了__slots__属性后,这个类的__dict__属性就不存在了(同理,该类的实例也不存在__dict__属性),如此一来,设置了__slots__属性的类的属性,只能是预先设定好的。

当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的小型数组来构建的,而不是为每个实例都定义一个__dict__字典,在__slots__中列出的属性名在内部被映射到这个数组的特定索引上。使用__slots__带来的副作用是我们没有办法给实例添加任何新的属性了。

注意:尽管__slots__看起来是个非常有用的特性,但是除非你十分确切的知道要使用它,否则尽量不要使用它。比如定义了__slots__属性的类就不支持多继承。__slots__通常都是作为一种优化工具来使用。--摘自《Python Cookbook》8.4

class A:
__slots__ = ['name', 'age'] a1 = A()
# print(a1.__dict__) # AttributeError: 'A' object has no attribute '__dict__'
a1.name = '张三'
a1.age = 24
# a1.hobby = '泡妞' # AttributeError: 'A' object has no attribute 'hobby'
print(a1.__slots__)

注意事项:
__slots__的很多特性都依赖于普通的基于字典的实现。
另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该只在那些经常被使用到的用作数据结构的类上定义__slots__,比如在程序中需要创建某个类的几百万个实例对象 。
关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。它更多的是用来作为一个内存优化工具。

__item__系列

class Foo:
def __init__(self, name):
self.name = name def __getitem__(self, item):
print(self.__dict__[item]) def __setitem__(self, key, value):
print('obj[key]=lqz赋值时,执行我')
self.__dict__[key] = value def __delitem__(self, key):
print('del obj[key]时,执行我')
self.__dict__.pop(key) def __delattr__(self, item):
print('del obj.key时,执行我')
self.__dict__.pop(item) f1 = Foo('sb')
print(f1.__dict__)
f1['age'] = 18
f1.hobby = '泡妞'
del f1.hobby
del f1['age']
f1['name'] = 'lqz'
print(f1.__dict__)

__init__

使用Python写面向对象的代码的时候我们都会习惯性写一个 __init__ 方法,__init__ 方法通常用在初始化一个类实例的时候。例如:

class Person:
def __init__(self, name, age):
self.name = name
self.age = age def __str__(self):
return '<Person: {}({})>'.format(self.name, self.age) p1 = Person('张三', 24)
print(p1)

上面是__init__最普通的用法了。但是__init__其实不是实例化一个类的时候第一个被调用的方法。当使用 Persion(name, age) 来实例化一个类时,最先被调用的方法其实是 __new__ 方法。

__new__

其实__init__是在类实例被创建之后调用的,它完成的是类实例的初始化操作,而 __new__方法正是创建这个类实例的方法

class Person:

    def __new__(cls, *args, **kwargs):
print('调用__new__,创建类实例')
return super().__new__(Person) def __init__(self, name, age):
print('调用__init__,初始化实例')
self.name = name
self.age = age def __str__(self):
return '<Person: {}({})>'.format(self.name, self.age) p1 = Person('张三', 24)
print(p1)

输出:

调用__new__,创建类实例
调用__init__,初始化实例
<Person: 张三(24)>

__new__方法在类定义中不是必须写的,如果没定义的话默认会调用object.__new__去创建一个对象(因为创建类的时候默认继承的就是object)。

如果我们在类中定义了__new__方法,就是重写了默认的__new__方法,我们可以借此自定义创建对象的行为。

举个例子:

重写类的__new__方法来实现单例模式。

class Singleton:
# 重写__new__方法,实现每一次实例化的时候,返回同一个instance对象
def __new__(cls, *args, **kw):
if not hasattr(cls, '_instance'):
cls._instance = super().__new__(Singleton)
return cls._instance def __init__(self, name, age):
self.name = name
self.age = age s1 = Singleton('张三', 24)
s2 = Singleton('李四', 20)
print(s1, s2) # 这两实例都一样
print(s1.name, s2.name)

__call__

__call__ 方法的执行是由对象后加括号触发的,即:对象()。拥有此方法的对象可以像函数一样被调用。

class Person:
def __init__(self, name, age):
self.name = name
self.age = age def __call__(self, *args, **kwargs):
print('调用对象的__call__方法') a = Person('张三', 24) # 类Person可调用
a() # 对象a可以调用

注意: 

__new__、__init__、__call__等方法都不是必须写的。

__doc__

定义类的描述信息。注意该信息无法被继承。

class A:
"""我是A类的描述信息"""
pass print(A.__doc__)

__iter__和__next__

如果一个对象拥有了__iter__和__next__方法,那这个对象就是可迭代对象

class A:
def __init__(self, start, stop=None):
if not stop:
start, stop = 0, start
self.start = start
self.stop = stop def __iter__(self):
return self def __next__(self):
if self.start >= self.stop:
raise StopIteration
n = self.start
self.start += 1
return n a = A(1, 5)
from collections import Iterator
print(isinstance(a, Iterator)) for i in A(1, 5):
print(i) for i in A(5):
print(i)
aaa=A(1)
print(next(aaa))
print(next(aaa)) #抛异常

__enter__和__exit__

一个对象如果实现了__enter__和___exit__方法,那么这个对象就支持上下文管理协议,即with语句

class A:
def __enter__(self):
print('进入with语句块时执行此方法,此方法如果有返回值会赋值给as声明的变量')
return 'oo' def __exit__(self, exc_type, exc_val, exc_tb):
print('退出with代码块时执行此方法')
print('', exc_type)
print('', exc_val)
print('', exc_tb) with A() as f:
print('进入with语句块')
# with语句中代码块出现异常,则with后的代码都无法执行。
# raise AttributeError('sb')
print(f) #f打印出oo
print('嘿嘿嘿')

上下文管理协议适用于那些进入和退出之后自动执行一些代码的场景,比如文件、网络连接、数据库连接或使用锁的编码场景等。

__len__

拥有__len__方法的对象支持len(obj)操作。

class A:
def __init__(self):
self.x = 1
self.y = 2 def __len__(self):
return len(self.__dict__) a = A()
print(len(a))

__hash__

拥有__hash__方法的对象支持hash(obj)操作。

class A:
def __init__(self):
self.x = 1
self.x = 2 def __hash__(self):
return hash(str(self.x) + str(self.x)) a = A()
print(hash(a))

__eq__

拥有__eq__方法的对象支持相等的比较操作

class A:
def __init__(self,x,y):
self.x = x
self.y = y def __eq__(self,obj):
# 打印出比较的第二个对象的x值
print(obj.x)
if self.x +self.y == obj.x+obj.y:
return True
else:
return False a = A(1,2)
b = A(2,1)
print(a == b)

Python面向对象之魔术方法的更多相关文章

  1. Python - 面向对象编程 - 魔术方法(双下划线方法)

    什么是魔术方法 在Python中,所有以 __ 双下划线包起来的方法,都统称为 Magic Method 魔术方法,也叫双下划线方法 有哪些重要的魔术方法? __new__ https://www.c ...

  2. Python 面向对象(三) 魔术方法

    __getitem__ 在对实例或对象使用索引访问时调用,self[key]__dir__ 收集当前模块的信息,包括继承自其它基类(包括object类)的属性和方法 __new 定义如何创建实例__i ...

  3. 十八、Python面向对象之魔术方法

    1.类的比较 class A(object): def __init__(self,value): self.value = value def __eq__(self,other): return ...

  4. Python 类的魔术方法

    Python中类的魔术方法 在Python中以两个下划线开头的方法,__init__.__str__.__doc__.__new__等,被称为"魔术方法"(Magic method ...

  5. Python学习笔记之面向对象编程(三)Python类的魔术方法

    python类中有一些方法前后都有两个下划线,这类函数统称为魔术方法.这些方法有特殊的用途,有的不需要我们自己定义,有的则通过一些简单的定义可以实现比较神奇的功能 我主要把它们分为三个部分,下文也是分 ...

  6. Python中的魔术方法详解

    介绍 在Python中,所有以“__”双下划线包起来的方法,都统称为“Magic Method”,中文称『魔术方法』,例如类的初始化方法 __init__ ,Python中所有的魔术方法均在官方文档中 ...

  7. python所有的魔术方法

    据说,Python 的对象天生拥有一些神奇的方法,它们总被双下划线所包围,他们是面向对象的 Python 的一切. 他们是可以给你的类增加魔力的特殊方法,如果你的对象实现(重载)了这些方法中的某一个, ...

  8. Python中的魔术方法详解(双下方法)

    介绍 在Python中,所有以“__”双下划线包起来的方法,都统称为“Magic Method”,中文称『魔术方法』,例如类的初始化方法 __init__ ,Python中所有的魔术方法均在官方文档中 ...

  9. python 面向对象、特殊方法与多范式、对象的属性及与其他语言的差异

    1.python 面向对象 文章内容摘自:http://www.cnblogs.com/vamei/archive/2012/06/02/2532018.html   1.__init__() 创建对 ...

随机推荐

  1. ovs-qos配置

    QoS配置 在许多网络场景中,都需要根据需求对网络流量部署服务质量(QoS)保障策略,比如限制指定主机的最大接入带宽等需求.本节将介绍如何在OVS上添加队列,并完成数据的入队操作,从而完成QoS策略部 ...

  2. Stanford Local 2016 E "Election of Evil"(搜索(正解)或并查集(划掉))

    传送门 题意: 给出集合U,V,集合U有n个元素,集合V有m个元素: 有 m 个操作,mi : s1 s2 有一条s1指向s2的边(s1,s2可能属于第三个集合,暂且称之为K集合): 指向边具有传递性 ...

  3. (BFS) leetcode 279. Perfect Squares

    Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 1 ...

  4. js上传图片压缩,并转化为base64

    <input type="file" onchange="startUpload(this,'front')" id="renm"/& ...

  5. DirectX11--HLSL中矩阵的内存布局和mul函数探讨

    前言 说实话,我感觉这是一个大坑,不知道为什么要设计成这样混乱的形式. 在我用的时候,以row_major矩阵,并且mul函数以向量左乘矩阵的形式来绘制时的确能够正常显示,并不会有什么感觉.但是也有人 ...

  6. 第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )

    一. 前言 在前面的两个章节中,我们分别详细介绍了EF的增删改的两种方式(方法和状态)和EF查询的两种方式( Lambda和Linq ),进行到这里,可以说对于EF,已经入门了,本来应该继续往下进行E ...

  7. SpringBoot系列: JdbcTemplate 快速入门

    对于一些小的项目, 我们没有必要使用MyBatis/JPA/Hibernate等重量级技术, 直接使用Spring JDBC 即可, Spring JDBC 是对 jdbc的简单封装, 很容易掌握. ...

  8. CF611D lcp+dp

    本篇博客只是留个辣鸡的自己标记一下,误入的同学请出门左转博客 https://blog.csdn.net/loy_184548/article/details/50865777 代码神马的也是复制啊 ...

  9. TCP/IP教程

    一.TCP/IP 简介 TCP/IP 是用于因特网的通信协议. 通信协议是对计算机必须遵守的规则的描述,只有遵守这些规则,计算机之间才能进行通信. 什么是 TCP/IP? TCP/IP 是供已连接因特 ...

  10. MySQL数据库学习2 - 数据库的操作

    一.系统数据库 二.创建数据库 三.数据库相关操作 四.了解内容 一.系统数据库 执行如下命令,查看系统库 show databases; information_schema: 虚拟库,不占用磁盘空 ...