Python细节小小知识点(四)

一、作用域
在一个模块中,我们可能会定义很多函数和变量,但有的函数和变量我们希望给别人使用,有的函数和变量我们希望仅仅在模块内部使用。在Python中,是通过_前缀来实现的。
正常的函数和变量名是公开的(public),可以被直接引用,比如:abc,x123,PI等;
类似__xxx__这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如__author__,__name__就是特殊变量,hello模块定义的文档注释也可以用特殊变量__doc__访问,我们自己的变量一般不要用这种变量名;
类似_xxx和__xxx这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc,__abc等;
之所以我们说,private函数和变量“不应该”被直接引用,而不是“不能”被直接引用,是因为Python并没有一种方法可以完全限制访问private函数或变量,但是,从编程习惯上不应该引用private函数或变量。
private函数或变量不应该被别人引用,那它们有什么用呢?请看例子:
| 1 | def _private_1(name): | 
我们在模块里公开greeting()函数,而把内部逻辑用private函数隐藏起来了,这样,调用greeting()函数不用关心内部的private函数细节,这也是一种非常有用的代码封装和抽象的方法,即:
外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public。
二、获取对象信息
获取对象信息优先使用isinstance(),之后是type、dir(),注意下面的几个方法。
2.1 使用dir()
如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法:
| 1 | >>> dir('ABC') | 
类似__xxx__的属性和方法在Python中都是有特殊用途的,比如__len__方法返回长度。在Python中,如果你调用len()函数试图获取一个对象的长度,实际上,在len()函数内部,它自动去调用该对象的__len__()方法,所以,下面的代码是等价的:
| 1 | >>> len('ABC') | 
我们自己写的类,如果也想用len(myObj)的话,就自己写一个__len__()方法:
| 1 | >>> class MyDog(object): | 
剩下的都是普通属性或方法,比如lower()返回小写的字符串:
| 1 | >>> 'ABC'.lower() | 
仅仅把属性和方法列出来是不够的,配合getattr()、setattr()以及hasattr(),我们可以直接操作一个对象的状态:
| 1 | >>> class MyObject(object): | 
紧接着,可以测试该对象的属性:
| 1 | >>> hasattr(obj, 'x') # 有属性'x'吗? | 
如果试图获取不存在的属性,会抛出AttributeError的错误:
| 1 | >>> getattr(obj, 'z') # 获取属性'z' | 
可以传入一个default参数,如果属性不存在,就返回默认值:
| 1 | >>> getattr(obj, 'z', 404) # 获取属性'z',如果不存在,返回默认值404 | 
也可以获得对象的方法:
| 1 | >>> hasattr(obj, 'power') # 有属性'power'吗? | 
2.2 小结
通过内置的一系列函数,我们可以对任意一个Python对象进行剖析,拿到其内部的数据。要注意的是,只有在不知道对象信息的时候,我们才会去获取对象信息。如果可以直接写:
| 1 | sum = obj.x + obj.y | 
就不要写:
| 1 | sum = getattr(obj, 'x') + getattr(obj, 'y') | 
一个正确的用法的例子如下:
| 1 | def readImage(fp): | 
假设我们希望从文件流fp中读取图像,我们首先要判断该fp对象是否存在read方法,如果存在,则该对象是一个流,如果不存在,则无法读取。hasattr()就派上了用场。
请注意,在Python这类动态语言中,根据鸭子类型,有read()方法,不代表该fp对象就是一个文件流,它也可能是网络流,也可能是内存中的一个字节流,但只要read()方法返回的是有效的图像数据,就不影响读取图像的功能。
| 1 | class Student(object): | 
类名本身也可以看作一个实例化对象

















