修改、查看私有属性、名字重整
如下,Test类定义 一个私有属性 __name 实例化一个对象 a ,无法调用该属性,
打印 a.__dict__( 可以检查一个对象的所有属性 )查看 ,发现__name存在并且名字变为 _Test__name (无法调用的原因,名字被改变)
改变规则:私有属性前添加类名,再在类名前添加一个下划线 ( 名字重整 )
我们验证一下,打印修改后的属性,如下
这里有个疑问,既然无法直接打印,那我们为什么可以直接修改?
修改测试一下,打印输出,此时__name 并不是私有属性了,此时的 __name 只是添加了一个属性,可以直接调用
这里就没有什么私有公有之说了,所谓的公有私有,是指类中的属性,实例对象无法直接调用
另外补充一下 __dict__属性,Test类也是一个对象,查看其属性:每个魔法属性对应值或函数
魔法属性、方法
无论人或事物往往都有不按套路出牌的情况,Python的类属性也是如此。存在一些具有特殊含义的属性
如上图,对应的魔法属性:
1. __doc__ :表示类的描述信息,help()方法也可以输出类的描述信息
class Test():
"""
This is a description of a class
"""
pass
print(Test.__doc__)
print("*" * 15)
print(help(Test))
2. __module__ 和 __class__ :__module__表示当前操作在哪个模块,__class__表示当前操作的对象的类是什么
# -*- coding:utf-8 -*-
# test.py
class Person(object):
def __init__(self):
self.name = 'laowang'
from test import Person
# main.py
obj = Person()
print(obj.__module__) # 输出创建该对象的对应模块 test
print(obj.__class__) # 输出创建该对象的对应模块中的类 test.Person
3. __init__ :初始化方法(不建议称其为构造方法,可以说__init__ 和 __new__ 方法一起完成了构造方法的功能),通过类创建对象时,自动触发执行
class Person:
def __init__(self, name):
self.name = name
self.age = 18
obj = Person('laowang') # 自动执行类中的 __init__ 方法
4. __del__:当对象在内存中被释放时,自动触发执行
注:此方法无需定义,因为python是一门高级语言,程序员在使用时,无需关系内存的分配和释放,因为此工作都是交给Python的解释器来执行,所以,__del__的调用是有解释器在进行垃圾回收时自动触发执行的。
class Foo:
def __del__(self):
pass
5. __call__:对象后面加括号,触发执行 (用于装饰器)
注:__init__方法的执行是由创建对象触发的,即 对象 = 类名() ; 而对于__call__ 方法执行是由对象后加括号触发的,即
对象() 或 类()
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print('__call__')
obj = Foo() # 执行 __init__
obj() # 执行 __call__
6. __dict__ : 类或对象中的所有属性
类的实例属性属于对象;类中的类属性和方法等属于类,即:
class Province(object):
country = 'China'
def __init__(self, name, count):
self.name = name
self.count = count
def func(self, *args, **kwargs):
print('func')
# 获取类的属性,即:类属性、方法、
print(Province.__dict__)
# 输出:{'__dict__':
, '__module__': '__main__', 'country': 'China', '__doc__': None, '__weakref__':
, 'func':
, '__init__':
}
obj1 = Province('山东', 10000)
print(obj1.__dict__)
# 获取 对象obj1 的属性
# 输出:{'count': 10000, 'name': '山东'}
obj2 = Province('山西', 20000)
print(obj2.__dict__)
# 获取 对象obj1 的属性
# 输出:{'count': 20000, 'name': '山西'}
7. __str__:如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值
class Foo:
def __str__(self):
return 'laowang'
obj = Foo()
print(obj)
# 输出:laowang
8. __getitem__、__setitem__、__delitem__ :用于索引操作,如字典。以上分别表示获取、设置、删除数据
# -*- coding:utf-8 -*-
class Foo(object):
def __getitem__(self, key):
print('__getitem__', key)
def __setitem__(self, key, value):
print('__setitem__', key, value)
def __delitem__(self, key):
print('__delitem__', key)
obj = Foo()
result = obj['k1'] # 自动触发执行 __getitem__
obj['k2'] = 'laowang' # 自动触发执行 __setitem__
del obj['k1'] # 自动触发执行 __delitem__
9. __getitem__、__setitem__、__delitem__ :该三个方法用于分片操作,如:列表
# -*- coding:utf-8 -*-
class Foo(object):
def __getslice__(self, i, j):
print('__getslice__', i, j)
def __setslice__(self, i, j, sequence):
print('__setslice__', i, j)
def __delslice__(self, i, j):
print('__delslice__', i, j)
obj = Foo()
obj[-1:1] # 自动触发执行 __getslice__
obj[0:1] = [11,22,33,44] # 自动触发执行 __setslice__
del obj[0:2] # 自动触发执行 __delslice__
With、上下文管理器
对于系统资源如文件、数据库连接、socket 而言,应用程序打开这些资源并执行完业务逻辑之后,必须做的一件事就是要关闭(断开)该资源。
比如 Python 程序打开一个文件,往文件中写内容,写完之后,就要关闭该文件,否则会出现什么情况呢?极端情况下会出现 "Too many open files" 的错误,因为系统允许你打开的最大文件数量是有限的。
同样,对于数据库,如果连接数过多而没有及时关闭的话,就可能会出现 "Can not connect to MySQL server Too many connections",因为数据库连接是一种非常昂贵的资源,不可能无限制的被创建。
普通版:
def m1():
f = open("output.txt", "w")
f.write("python之禅")
f.close()
这样写有一个潜在的问题,如果在调用 write 的过程中,出现了异常进而导致后续代码无法继续执行,close 方法无法被正常调用,因此资源就会一直被该程序占用者释放。
进阶版:
def m2():
f = open("output.txt", "w")
try:
f.write("python之禅")
except IOError: # except 未捕获到对应异常,依然会崩
print("oops error")
finally:
f.close()
改良版本的程序是对可能发生异常的代码处进行 try 捕获,使用 try/finally 语句,该语句表示如果在 try 代码块中程序出现了异常,后续代码就不再执行,而直接跳转到 except 代码块。而无论如何,finally 块的代码最终都会被执行。因此,只要把 close 放在 finally 代码中,文件就一定会关闭。
高级版:
def m3():
with open("output.txt", "r") as f:
f.write("Python之禅")
一种更加简洁、优雅的方式就是用 with 关键字。 open 方法的返回值赋值给变量 f,当离开 with 代码块的时候,系统会自动调用 f.close() 方法, with 的作用和使用 try/finally 语句是一样的。那么它的实现原理是什么?在讲 with 的原理前要涉及到另外一个概念,就是 上下文管理器(Context Manager) 。
上下文(context)
上下文在不同的地方表示不同的含义,要感性理解。context其实说白了,和文章的上下文是一个意思,通俗一点,理解为环境
上下文管理器
任何实现了 __enter__() 和 __exit__() 方法的对象都可称之为上下文管理器 ,上下文管理器对象可以使用 with 关键字。显然,文件(file)对象也实现了上下文管理器。
那么文件对象是如何实现这两个方法的呢?我们可以模拟实现一个自己的文件类,让该类实现 __enter__() 和 __exit__() 方法。
class File():
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
def __enter__(self):
print("entering")
self.f = open(self.filename, self.mode)
return self.f
def __exit__(self, *args):
print("will exit")
self.f.close()
__enter__() 方法返回资源对象,这里就是你将要打开的那个文件对象,__exit__() 方法处理一些清除工作。
因为 File 类实现了上下文管理器,现在就可以使用 with 语句了。
with File('out.txt', 'w') as f:
# 实例化File对象,执行__init__方法,filename属性对应out.txt,w对应mode属性
# with判断其是否是一个上下文管理器(实现对应的方法),然后自动调用 __enter__()方法
# __enter__()方法返回值是什么,对应的 f 就是什么,而当结束或者出现异常时,会自动调用 __exit__()
# 方法
print("writing")
f.write('hello, python')
这样,你就无需显示地调用 close 方法了,由系统自动去调用,哪怕中间遇到异常 close 方法也会被调用。
实现上下文管理器的另外方式(了解)
Python 还提供了一个 contextmanager 的装饰器,更进一步简化了上下文管理器的实现方式。通过 yield 将函数分割成两部分,yield 之前的语句在 __enter__ 方法中执行,yield 之后的语句在 __exit__ 方法中执行。紧跟在 yield 后面的值是函数的返回值。
from contextlib import contextmanager
@contextmanager
def my_open(path, mode):
f = open(path, mode)
yield f
f.close()
调用
with my_open('out.txt', 'w') as f:
f.write("hello , the simplest context manager")
总结
Python 提供了 with 语法用于简化资源操作的后续清除操作,是 try/finally 的替代方法,实现原理建立在上下文管理器之上。 此外,Python 还提供了一个 contextmanager 装饰器,更进一步简化上下管理器的实现方式。