Python从进阶到高级—通俗易懂版(三)
# ==================================
# Author : Mikigo
# Env :deepin 20.4 os
# ==================================
1、破解私有属性
私有属性就是在类的内部能访问,外部不能访问。
在 Python 中没有专门的语句进行私有化,而通过在属性或方法前面加“两个下划线”实现。
举例:
class Test:
def __init__(self):
self.__mi = "Mikigo"
def __ki(self):
print("Mikigo")
def go(self):
print(self.__mi)
Test().go()
Mikigo
你看,在类的内部访问私有属性是可以正常拿到的,方法也是一样的。
现在我们访问私有属性试试:
Test().__mi
Traceback (most recent call last):
File "/tmp/pycharm_project_609/123.py", line 6, in <module>
print(Test().__mi)
AttributeError: 'Test' object has no attribute '__mi'
从外部进行私有属性访问是不行的,人家是私有的。
Test().__ki()
Traceback (most recent call last):
File "/tmp/pycharm_project_609/123.py", line 9, in <module>
Test().__ki()
AttributeError: 'Test' object has no attribute '__ki'
私有方法也无法访问,没问题哈。
有同学要问了,我就是想访问,越是私有的我越想看,怎么才能看到别人的隐私,快说!
泄露天机了哈,这是 Python 一种很奇妙的结构化处理,为什么说是结构化处理,实际上 Python 拿到双下划线之后,对其进行了变形,在前面加了一个下划线和类名,我们通过这种方式可以访问:
print(Test()._Test__mi)
Test()._Test__ki()
Mikigo
Mikigo
你看,这样就可以正常访问了,但是既然作者不希望使用者调用这个方法,我们也尽量不要去强行使用它,强扭的瓜不甜。
所以说,从语言的角度是没有绝对的安全,任何语言都是这样,更多的是一种编程上的约束。
通常在大多数实践中,我们更倾向于使用一个下划线来表示私有属性,这不是真正的私有,而是一种更友好的编程规范,社区称之为 “受保护的”属性,它向使用着表达了这是一个私有的方法,但是你仍然可以使用它,这就是社区,这就是开源,respect~。
2、对象的自省机制
自省(introspection),即自我反省,而对象的自省实际上就是查看对象实现了哪些属性或方法。
简单讲就是,告诉别人:我是谁,我能干啥。
Python 的常用的自省函数有四个:dir()、type()、 hasattr()、isinstance()
(1)isinstance() 和 type() 前面也提到过,这里不讲了。
(2)dir() 是最为常用的一个自省函数:
引用前面的 Test 类
print(dir(Test))
['_Test__ki', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'go']
除了 _Test__ki
和 go
方法以外,其他的方法都是魔法函数,即最开始我们提到的协议,你看随便一个对象就实现了这么多协议,是不是很神奇。
(3)hasattr() 主要用于判断对象中是否包含某个属性,返回布尔值。
print(hasattr(Test, "go"))
print(hasattr(Test, "wo"))
True
False
很简单,不多讲哈。
其他还有一些自省函数可以了解一下,偶尔用到也挺好的:
__doc__
获取到文档字符串;__name__
获取对象的名称;__dict__
包含了类里可用的属性名-属性的字典;__bases__
返回父类对象的元组;但不包含继承树更上层的其他类。
3、super
super 函数是用于调用父类的一个方法。
class A:
def mi(self):
print("=== mi ===")
class B(A):
def ki(self):
super().mi()
B().ki()
=== mi ===
super 的使用方法是很简单的,但是如果涉及到多继承的情况下,就要小心处理。
准确的讲它不是调用父类的方法,而是调用的 MRO
顺序上的下一个方法。
4、上下文管理器
在讲到上下文管理器的时候,经常有同学一脸懵,然后我说 with
的时候,就会脱口而出 with open
。
没错,with 语句用得最多的也是这个,它是 Python 提供的一种处理资源回收的神奇方法,如果没有 with 我们可能需要多写很多代码。
大家都知道打开一个文件之后是需要关闭的,但是在操作文件的过程中很容易报错,这时候我们需要进行异常处理,要保证无论是否存在异常的情况下,文件都能正常的被关闭,我们几乎只能使用try里面的finally来处理:
f = open("test.txt", "w")
try:
f.write(some_txt)
except:
pass
finally:
f.close()
如果用 with 语句处理就会很简单:
with open("test.txt", "w") as f:
f.write(some_txt)
对比起来,哪个更好不用多说,自己品。
在《流畅的 Python》这本书里面提到:
在任何情况下,包括CPython,最好显式关闭文件;而关闭文件的最可靠方式是使用with语句,它能保证文件一定会被关闭,即使打开文件时抛出了异常也无妨。
那我们如何实现一个上下文管理器呢?
- 基于类实现上下文管理器
要实现上下文管理器,需要实现两个魔法函数:__enter__
和 __exit__
。
看名称就知道了,enter 就是进入的时候要做的事情,exit 就是退出的时候要做的事情,很好记有没有。
class Context:
def __init__(self, file_name):
self.file_name = file_name
self.f = None
def __enter__(self):
print("进入 with")
self.f = open(self.file_name, "r")
return self.f
def __exit__(self, exc_type, exc_val, exc_tb):
print("退出 with")
if self.f:
self.f.close()
然后我们就可以使用 with 语句
with Context("test.txt") as f:
print(f.read())
进入 with
我是一个测试文件
退出 with
完美哈,一个上下文管理器的类就轻松搞定。
- 基于 contextlib 实现上下文管理器
还有种通过标准库实现上下文管理器的方法:
from contextlib import contextmanager
@contextmanager
def context_test(file_name):
print("进入 with")
try:
f = open(file_name, "r")
yield f
finally:
print("退出 with")
f.close()
来用 with 玩耍一下
with context_test("test.txt") as f:
print(f.read())
进入 with
我是一个测试文件
退出 with
利用生成器的原理,yield 之前是进入,yield 之后是退出,同样可以实现一个上下文管理器,稍微理解一下哈。
上下文管理器是 Python 提供给我们的一个非常方便且有趣的功能,经常被用在打开文件、数据库连接、网络连接、摄像头连接等场景下。如果你经常做一些固定的开始和结尾的动作,可以尝试一下。
本文来自博客园,作者:mikigo,转载请注明原文链接:https://www.cnblogs.com/mikigo/p/16812948.html