一、__getattribute__

object.__getattribute__(self, name)

无条件被调用,通过实例访问属性。如果class中定义了__getattr__(),则__getattr__()不会被调用(除非显示调用或引发AttributeError异常)

  
  class Foo:

def __init__(self,x):
self.x = x

def __getattr__(self, item):
print("执行__getattr__")

def __getattribute__(self, item):
print("执行__getattribute__")
# raise AttributeError("不存在的异常")

f = Foo("nick")
print(f.x)
print(f.xxxx)#这里本来调用后应该在__getattribute__中产生AttributeError,然后调用__getattr__,但在#这里__getattribute__被重写了,没有产生AttributeError,所以就没调用__getattr__

  

输出结果

  执行__getattribute__
None
执行__getattribute__
None

  

object.__getattr__(self, name)

当一般位置找不到attribute的时候,会调用getattr,返回一个值或AttributeError异常。

  
  class Foo:

def __init__(self,x):
self.x = x

def __getattr__(self, item):
print("执行__getattr__")

# def __getattribute__(self, item):
# print("执行__getattribute__")
# raise AttributeError("不存在的异常")

f = Foo("nick")
print(f.x)
print(f.xxxx) #这里是执行了新定义的__getattr__方法,并没有直接报错

  

输出结果

  
  nick
执行__getattr__
None

  

每次通过实例访问属性,都会经过__getattribute__函数。

而当属性不存在时,仍然需要访问__getattribute__,不过接着要访问__getattr__。这就好像是一个异常处理函数。 需要注意的是,当使用类访问不存在的变量是,不会经过__getattr__函数。

当属性存在时,经过__getattribute__函数,返回属性的值。

二、__setitem__,__getitem,__delitem__

在类中,对象以object["key"]形式访问属性或方法时调用item系列内置方法,对象以object.属性(以点的形式)调用属性或方法时调用attr系列内置方法。

  
  class Foo:

def __init__(self,name):
self.name = name

def __getitem__(self, item):
print("执行__getitem__")
return self.__dict__[item]

def __setitem__(self, key, value):
print("执行__setitem__")
self.__dict__[key] = value

def __delitem__(self, key):
print("执行__delitem__")
self.__dict__.pop(key)

f = Foo("nick")
print(f["name"]) #获取属性, 执行__getitem__方法
f["age"] = 18 #设置属性,执行__setitem__方法
print(f.__dict__)
del f["name"] #删除属性,执行__delitem__方法
print(f.__dict__)

  

输出结果

  执行__getitem__
nick
执行__setitem__
{'name': 'nick', 'age': 18}
执行__delitem__
{'age': 18}

  

三、__str__,__repr__,__format__

__str__是控制改变对象的字符串显示

print(obj)/'%s'%obj/str(obj)时,实际上是内部调用了obj.__str__方法,如果str方法有,那么他返回的必定是一个字符串,如果没有__str__方法,会先找本类中的__repr__方法,再没有再找父类(object)中的__str__

例子1

  class Foo:
def __str__(self):
return "控制打印对象时的显示形式"

f = Foo()
print(f) #输出结果: 控制打印对象时的显示形式

  

分析:这里打印对象直接调用了被重新定义的__str__方法

例子2

  class Foo:
def __str__(self):
return "控制打印对象时的显示形式"

f = Foo()
a = str(f)
print(a) #输出结果: 控制打印对象时的显示形式

  

分析:这里的str(object)也是调用了__str__方法

例子3

  
  class Foo:
def __str__(self):
return "控制打印对象时的显示形式"

f = Foo()
print("%s"%f) #输出结果: 控制打印对象时的显示形式

  

分析:这里的“%s”%f也是调用了__str__方法

__repr__也是显示对象的名字方式,用repr(object)或者“%r”%object会调用__repr__方法

repr 是str的备胎,但str不能做repr的备胎

例子4

  class Foo:

def __str__(self):
return "控制打印对象时的显示形式"

def __repr__(self):
print("在执行repr")
return "repr Foo"
pass
f = Foo()
print("%r"%f)
print(f)

  

输出结果

  
  在执行repr
repr Foo
控制打印对象时的显示形式

  

分析:这里print("%r"%f) 调用重新定义的__repr__方法,print(f)调用__str__方法

例子5

  
  class Foo:

def __str__(self):
return "控制打印对象时的显示形式"

def __repr__(self):
print("在执行repr")
return "repr Foo"
pass
f = Foo()
print(repr(f))

  

输出结果

  在执行repr
repr Foo

  

例子6

  
  class Foo:

# def __str__(self):
# return "控制打印对象时的显示形式"

def __repr__(self):
print("在执行repr")
return "repr Foo"
pass
f = Foo()
print(f)

  

输出结果

  在执行repr
repr Foo

  

分析:如果要调用__str__方法,首先在本类中查找有没有__str__方法,如果没有就找本类有没有__repr__方法,如果有就执行__repr__方法,不再借用父类的__str__方法。所以repr 是str的备胎,但str不能做repr的备胎。

例子7

  
  class Foo:

def __str__(self):
return "控制打印对象时的显示形式"

# def __repr__(self):
# print("在执行repr")
# return "repr Foo"
pass
f = Foo()
print(repr(f)) #输出结果 <__main__.Foo object at 0x002EB550>

  

分析:要调用__repr__方法,本类没有,直接借用父类的__repr__方法,而不执行__str__方法。所以repr 是str的备胎,但str不能做repr的备胎。

__format__

自定制格式化字符串__format__,

  format_dic = {
"ymd":"{0.year}{0.month}{0.day}",
"dmy":"{0.day}:{0.month}:{0.year}",
"mdy":"{0.month}-{0.day}-{0.year}"
}

class Foo:

def __init__(self,year,month,day):
self.year = year
self.month = month
self.day = day

def __format__(self, format_spec):
if not format_spec or format_spec not in format_dic:
format_spec = "ymd"
fmt = format_dic[format_spec]
return fmt.format(self)

f = Foo(2018,8,20)
print(format(f))
print(format(f,"dmy"))
print(format(f,"mdy"))
print("{:mdy}".format(f))

  

输出结果

  
  2018820
20:8:2018
8-20-2018
8-20-2018

  

分析:调用format方法自定制字符串格式化,这里调用__format__方法

四、__slots__

__slots__:其实就是将类中的名称锁定,实例化对象,只可以赋值和调用,不可以删除属性和增加新的属性

1、__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(也就是意味着所有实例只有一个数据属性)

2、使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)

3、字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。

4、应用场景:当实例化几万个对象的时候,每个对象都会生成一个名称空间__dict__,而每一个名称空间都会各自占用一个内存,造成内存的浪费,用 __slots__,不用再产生每个对象的 dict 了,省内存,对象的 dict 都统一用类的 dict,属性都是用 slots 给定义的

  
  class Foo:

__slots__ = ["name","age"] #在类中定义属性"name"、"age"

f1 = Foo()
f1.name = "nick" #首先要进行赋值,不赋值调用会出错
print(f1.name)
# f1.gender = "female" # 新增__slots__之外的属性会报错
# del f1.age #删除属性也会报错

  

五、__doc__

__doc__显示文档注释信息,文档注释信息无法继承给子类。

例子

  
  class Foo:
"""
我是文档注释
"""
pass

f = Foo()
print(f.__doc__) #输出结果 : 我是文档注释

  

例子2

  
  class Foo:
"""
我是文档注释
"""
pass

class Son(Foo):
pass

s = Son()
print(s.__doc__) #输出结果:None

  

六、__module____class__

 __module__ 表示当前操作的对象在那个模块

  __class__ 表示当前操作的对象的类是什么

例子1

  
  class Foo:

def __init__(self,name):
self.name = name

f = Foo("nick")
print(f.__module__) #当执行文件为当前文件时,__module__ = __main__
#输出结果:__main__
print(f.__class__) #输出当前操作的类名是什么
#输出结果:<class '__main__.Foo'>

  

例子2

文件结构

  ├── index.py #执行文件
│ ├── test
│ │   ├── a.py  

a.py内容:

  
  class Bar:
def __init__(self):
self.name = "nicholas"

  

index.py内容:

 from test.a import  Bar

class Foo:

def __init__(self,name):
self.name = name

b = Bar()
print(b.name) #输出结果 nicholas
print(b.__module__) #输出结果 test.a ,表示当前操作的类在test.a下
print(b.__class__) #表示当前操作的类名是<class 'test.a.Bar'>

  

七、 __del__析构方法

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

这个是在回收实例化对象时触发执行的方法,删除对象的属性不会触发__del__方法

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

例子1

  
  class Foo:
def __init__(self,name):
self.name = name

def __del__(self):
print("我执行啦!")

f = Foo("nick")
print("-----1")
del f.name
print("-----2") #这里是先执行print("-----1")然后执行print("-----2") ,
# 执行del f.name只是删除对象字典的"name"属性,并不会触发__del__方法

  

输出结果

  
  -----1
-----2
我执行啦!

  

例子2

  
  class Foo:
def __init__(self,name):
self.name = name

def __del__(self):
print("我执行啦!")

f = Foo("nick")
print("-----1")
del f #删除对象才能调用__del__,这里既执行了__del__方法,又删除了对象
print("-----2")

  

输出结果

  -----1
我执行啦!
-----2

  

例子3

  
  class Foo:
def __init__(self,name):
self.name = name

def __del__(self):
print("我执行啦!")

f = Foo("nick")
import time
time.sleep(2)

  

输出结果


 我执行啦!

  

分析:这里在程序结束时,python解释器自动回收内存,执行了__del__方法

典型的应用场景:

创建数据库类,用该类实例化出数据库链接对象,对象本身是存放于用户空间内存中,而链接则是由操作系统管理的,存放于内核空间内存中

当程序结束时,python只会回收自己的内存空间,即用户态内存,而操作系统的资源则没有被回收,这就需要我们定制__del__,在对象被删除前向操作系统发起关闭数据库链接的系统调用,回收资源。

例子

  
  class Foo:

def __del__(self):
print("我执行了")

f = Foo()
f.file = open("test.txt") #打开文件,在操作系统中打开了一个文件,
# 拿到了文件操作符存在了内存中
del f.file #删除了内存中的文件操作符,但是并未关闭操作系统中的文件,
# 这时需要在__del__中加入定制f.file.close(),关闭操作系统的文件
print("-----1")
#最后程序结束python解释器自动执行内存回收,执行了__del__方法

  

输出结果

  -----1
我执行了

  

Python之路(第二十六篇) 面向对象进阶:内置方法的更多相关文章

  1. Python之路(第二十九篇) 面向对象进阶:内置方法补充、异常处理

    一.__new__方法 __init__()是初始化方法,__new__()方法是构造方法,创建一个新的对象 实例化对象的时候,调用__init__()初始化之前,先调用了__new__()方法 __ ...

  2. Python之路(第二十八篇) 面向对象进阶:类的装饰器、元类

    一.类的装饰器 类作为一个对象,也可以被装饰. 例子 def wrap(obj): print("装饰器-----") obj.x = 1 obj.y = 3 obj.z = 5 ...

  3. Python之路(第二十五篇) 面向对象初级:反射、内置方法

    [TOC] 一.反射 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力(自省).这一概念的提出很快引发了计算机科学领域关于应用反射性的研究.它 ...

  4. Python之路(第二十四篇) 面向对象初级:多态、封装

    一.多态 多态 多态:一类事物有多种形态,同一种事物的多种形态,动物分为鸡类,猪类.狗类 例子 import abc class H2o(metaclass=abc.ABCMeta): ​ def _ ...

  5. Python之路(第二十二篇) 面向对象初级:概念、类属性

    一.面向对象概念 1. "面向对象(OOP)"是什么? 简单点说,“面向对象”是一种编程范式,而编程范式是按照不同的编程特点总结出来的编程方式.俗话说,条条大路通罗马,也就说我们使 ...

  6. Python之路(第十六篇)xml模块、datetime模块

    一.xml模块 xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单, xml比较早,早期许多软件都是用xml,至今很多传统公司如金融行业的很多系统的接口还主要 ...

  7. Python之路【第六篇】:socket

    Python之路[第六篇]:socket   Socket socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字&quo ...

  8. Python开发【第二十二篇】:Web框架之Django【进阶】

    Python开发[第二十二篇]:Web框架之Django[进阶]   猛击这里:http://www.cnblogs.com/wupeiqi/articles/5246483.html 博客园 首页 ...

  9. python中有两个下划线__的是内置方法,一个下划线_或者没有下划线的可能是属性,也可能是方法,也可能是类名

    python中有两个下划线__的是内置方法,一个下划线_或者没有下划线的可能是属性,也可能是方法,也可能是类名,如果在类中定义的就是类的私有成员. >>> dir(__builtin ...

随机推荐

  1. numpy中的数学

    1.dot,exp v = np.dot(arg1,arg2) #矩阵乘法 v2 = np.exp() # e的-x 次方

  2. jenkins动态参数插件Dynamic Parameter安装及简单使用

    插件安装: 1.先下载插件hpi文件到本地   jenkins插件下载地址 http://mirror.xmission.com/jenkins/plugins/ http://updates.jen ...

  3. tomcat端口被占用 实际上却查询不到此端口

    参考 http://www.cnblogs.com/jieliujas/p/9413064.html 1.打开PCHunter,在进程选项卡下面可以找到无法结束的进程,右键--强制结束,失败(这儿效果 ...

  4. 每月IT摘录201810

    技术 1.Redis.对于单机实例,我们采用原生主从(Master-Slave)模式实现高可用,常规模式下对外仅暴露 Master 节点.由于使用原生 Redis,所以单机实例支持所有 Redis 指 ...

  5. for 与 for in

    在JavaScript中提供了两种方式迭代对象: (1)for 循环: (2)for..in循环: 使用for循环进行迭代数组对象,想必大家都已经司空见惯了.但是,使用for.. in循环时,大家可要 ...

  6. pta l2-16(愿天下有情人都是失散多年的兄妹)

    题目链接:https://pintia.cn/problem-sets/994805046380707840/problems/994805061769609216 题意:两个异性的人五服之内不得通婚 ...

  7. hdoj1004(查找众多字符串中个数最多的字符串)

    Let the Balloon Rise. 最近开始刷hdoj,想通过写博客做做笔记,记录写过代码. Problem Description Contest time again! How excit ...

  8. centos 7 下 Ceph 配置安装

    一.环境介绍 系统: CentOS Linux release 7.3.1611 (Core) 硬盘: 系统盘:300GB*2-raid 1 OSD:600GB*4-raid 5      ceph ...

  9. tf.layers.dense()

    tf.layers.dense用法 2018年05月30日 19:09:58 o0haidee0o 阅读数:20426   dense:全连接层 相当于添加一个层,即初学的add_layer()函数 ...

  10. LibreOJ 6282. 数列分块入门 6

    题目链接:https://loj.ac/problem/6282 参考博客:http://www.cnblogs.com/stxy-ferryman/p/8560551.html 这里如果用数组的话元 ...