工作中,我们使用一些之前没用到过的模块,使用时需要了解一下这个模块中的一些类的方法或属性,怎么做呢?目前我比较常用的两款IDE“Pycharm”和“VSCode”,都可以通过先导包,然后通过“Ctrl+鼠标左键”,进入源码后观看并膜拜一下大神们的代码,当然也可以进入我们在项目中自己所定义的,然后进行快速修改,真的是很方便呢。但是有的时候,我们使用的环境没有这类的IDE,那该怎么学习我们要用的这些类方法和属性呢?方法当然很多,无论是小白,还是大神,百度谷歌大法都是比较快速和方便的。但是对于一些刚开源的或者是我们自己定义的呢,这里我们就聊聊Python 中的内建函数——dir 函数
首先可以先通过简单的源码解读,可以得知:
1.他的返回值是一个元素为字符串的列表
2.当传入一个模块对象时,返回的是模块里面所有的属性(变量名和方法)
我在function_use 这个文件夹或者包中创建了一个模块(demo01.py),内容随便定义几个变量和函数及类,如:
1 a = 10 2 b = \'test\' 3 4 def c(x): 5 print(x) 6 7 class D(object): 8 def __init__(self): 9 self.name = \'name\' 10 self.age = 18 11 12 def get_name(self): 13 return self.name 14 15 class E(D): 16 pass
然后再创建一个模块(demo02.py),并在"demo02.py"中引用"demo01.py“,然后打印dir(demo01),如:
1 from function_use import demo01 2 3 print(dir(demo01)) 4 5 6 [\'D\', \'E\', \'__builtins__\', \'__cached__\', \'__doc__\', \'__file__\', \'__loader__\', \'__name__\', \'__package__\', \'__spec__\', \'a\', \'b\', \'c\']
3.当传入的对象是一个类时,返回这个类及其所有父类(包括父类的父类)的属性和方法
就上面的例子,无论是在当前模块“demo01.py”下面调用:
1 ... 2 if __name__ == \'__main__\': 3 print(dir(D)) 4 5 [\'__class__\', \'__delattr__\', \'__dict__\', \'__dir__\', \'__doc__\', \'__eq__\', \'__format__\', \'__ge__\', \'__getattribute__\', \'__gt__\', \'__hash__\', \'__init__\', \'__le__\', \'__lt__\', \'__module__\', \'__ne__\', \'__new__\', \'__reduce__\', \'__reduce_ex__\', \'__repr__\', \'__setattr__\', \'__sizeof__\', \'__str__\', \'__subclasshook__\', \'__weakref__\', \'get_name\']
还是在“demo02.py” 中引用的调用
1 from function_use import demo01 2 3 # print(dir(demo01)) 4 print(dir(demo01.D)) 5 6 [\'__class__\', \'__delattr__\', \'__dict__\', \'__dir__\', \'__doc__\', \'__eq__\', \'__format__\', \'__ge__\', \'__getattribute__\', \'__gt__\', \'__hash__\', \'__init__\', \'__le__\', \'__lt__\', \'__module__\', \'__ne__\', \'__new__\', \'__reduce__\', \'__reduce_ex__\', \'__repr__\', \'__setattr__\', \'__sizeof__\', \'__str__\', \'__subclasshook__\', \'__weakref__\', \'get_name\']
可以看出,结果是一样的。
4.当传入的对象是其他的时候(照我的理解,这个其他对象,就是一个实例对象),则返回这个实例对象的属性和方法,实例对象类的属性和方法,以及这个类的所有基类的属性和方法
相对于第三种情况(传入的对象是一个类时)其实只是多了这个实例对象的属性,感觉绕的话,就看下面的例子:
老样子,在”demo01.py“ 里面
1 ... 2 if __name__ == \'__main__\': 3 e = E() 4 print(dir(e)) 5 6 [\'__class__\', \'__delattr__\', \'__dict__\', \'__dir__\', \'__doc__\', \'__eq__\', \'__format__\', \'__ge__\', \'__getattribute__\', \'__gt__\', \'__hash__\', \'__init__\', \'__le__\', \'__lt__\', \'__module__\', \'__ne__\', \'__new__\', \'__reduce__\', \'__reduce_ex__\', \'__repr__\', \'__setattr__\', \'__sizeof__\', \'__str__\', \'__subclasshook__\', \'__weakref__\', \'age\', \'get_name\', \'name\']
或者在”demo02.py“ 里面
1 from function_use import demo01 2 3 # print(dir(demo01)) 4 e = demo01.E() 5 print(dir(e)) 6 7 [\'__class__\', \'__delattr__\', \'__dict__\', \'__dir__\', \'__doc__\', \'__eq__\', \'__format__\', \'__ge__\', \'__getattribute__\', \'__gt__\', \'__hash__\', \'__init__\', \'__le__\', \'__lt__\', \'__module__\', \'__ne__\', \'__new__\', \'__reduce__\', \'__reduce_ex__\', \'__repr__\', \'__setattr__\', \'__sizeof__\', \'__str__\', \'__subclasshook__\', \'__weakref__\', \'age\', \'get_name\', \'name\']
可以看到,只是多了E 的实例对象的两个属性“age” 和“name”
===================问题分割线===================
这里就有一个待解决的问题,就上面的例子,我尝试了在demo01.py 模块的D 这个类里面加了一个类方法及类属性
1 a = 10 2 b = \'test\' 3 4 5 def c(x): 6 print(x) 7 8 9 class D(object): 10 dd = 123 11 12 def __init__(self): 13 self.name = \'name\' 14 self.age = 18 15 16 def get_name(self): 17 return self.name 18 19 @classmethod 20 def print_x(cls): 21 print("x") 22 23 24 class E(D): 25 pass 26 27 28 if __name__ == \'__main__\': 29 # e = E() 30 # print(dir(e)) 31 print(dir(E)) 32 E.print_x() 33 # print(E.get_name()) 34 35 36 [\'__class__\', \'__delattr__\', \'__dict__\', \'__dir__\', \'__doc__\', \'__eq__\', \'__format__\', \'__ge__\', \'__getattribute__\', \'__gt__\', \'__hash__\', \'__init__\', \'__le__\', \'__lt__\', \'__module__\', \'__ne__\', \'__new__\', \'__reduce__\', \'__reduce_ex__\', \'__repr__\', \'__setattr__\', \'__sizeof__\', \'__str__\', \'__subclasshook__\', \'__weakref__\', \'dd\', \'get_name\', \'print_x\'] 37 38 x # E.print_x()
这都没问题,但是我去调用dir 函数返回的“get_name” 函数时,却提示我必须要传入一个必传的参数“self”这样就需要
print(E.get_name(E()))了,但是这样的话,为什么这个方法可以出现在dir(E)的返回值里面呢?
有点晕
===================问题分割线===================
说完这个函数的返回值,我们再聊聊其中具体的内容,这里用自定义的例子不好说明,就搬来廖老师的例子吧
首先对一个字符串对象,比如“ABC” 使用dir()函数,查看他的所有属性和方法
1 print(dir("ABC")) 2 3 [\'__add__\', \'__class__\', \'__contains__\', \'__delattr__\', \'__dir__\', \'__doc__\', \'__eq__\', \'__format__\', \'__ge__\', \'__getattribute__\', \'__getitem__\', \'__getnewargs__\', \'__gt__\', \'__hash__\', \'__init__\', \'__iter__\', \'__le__\', \'__len__\', \'__lt__\', \'__mod__\', \'__mul__\', \'__ne__\', \'__new__\', \'__reduce__\', \'__reduce_ex__\', \'__repr__\', \'__rmod__\', \'__rmul__\', \'__setattr__\', \'__sizeof__\', \'__str__\', \'__subclasshook__\', \'capitalize\', \'casefold\', \'center\', \'count\', \'encode\', \'endswith\', \'expandtabs\', \'find\', \'format\', \'format_map\', \'index\', \'isalnum\', \'isalpha\', \'isdecimal\', \'isdigit\', \'isidentifier\', \'islower\', \'isnumeric\', \'isprintable\', \'isspace\', \'istitle\', \'isupper\', \'join\', \'ljust\', \'lower\', \'lstrip\', \'maketrans\', \'partition\', \'replace\', \'rfind\', \'rindex\', \'rjust\', \'rpartition\', \'rsplit\', \'rstrip\', \'split\', \'splitlines\', \'startswith\', \'strip\', \'swapcase\', \'title\', \'translate\', \'upper\', \'zfill\']
可以看出来,字符串对象的属性和方法还是很多的,类似__xx__的属性和方法在Python 中都是有特殊用途的,比如__len__方法返回长度。在Python 中,如果你调用len() 函数试图获得一个对象的长度,实际上,在len() 函数内部,它自动去调用该对象的__len__() 方法,所以得到两个结论:
1.下面的代码是等价的
1 print(len("ABC")) # 3 2 print("ABC".__len__()) # 3
2.对一个对象使用dir 函数,返回的列表里面,如果没有__len__ 方法,我们去对这个对象使用len 函数,就会报TypeError 的错
比如对整数类型使用len 函数,或者是上面我们"demo01.py" 里面的"e" 使用len 函数,如果我们想用len(e) 的话,就要自己写一个__len__() 方法:
1 class D(object): 2 dd = 123 3 4 def __init__(self): 5 self.name = \'name\' 6 self.age = 18 7 8 def __len__(self): 9 return 100 10 11 def get_name(self): 12 return self.name 13 14 @classmethod 15 def print_x(cls): 16 print("x") 17 18 19 class E(D): 20 pass 21 22 if __name__ == \'__main__\': 23 e = E() 24 print(len(e)) # 100
除了这些“__xx__” 特殊格式的方法,剩下的都是普通属性或方法,比如lower() 返回小写的字符串
1 print("ABC".lower()) # \'abc\'
未完