Python学习之内置类属性和类方法
✨ python3与python2其中的一个区别就是python3默认继承object这个类。我们可以在object的源码中看到一大堆__ xxx __的方法 和 属性,这些方法就是类的内置方法和内置属性。
一、什么是内置类属性
当python创建一个类之后,系统就自带了一些属性,叫内置类属性。这些属性名用双下划线包括并与普通属性名区分。通常的属性包括:
1. _dict_ 的用法
__dict__可以作用在文件、类或者类的对象上,最终返回的结果为一个字典。
- 对于类而言,类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类__dict__里的;
- 对于类的对象而言,对象的__dict__中存储了关于self.xxx变量的信息;
class TestName: #🐿️ 类属性 a = 2 b = 2 #🐿️ 初始化方法 def __init__(self,c): self.a = 0 self.b = 1 #实例属性 self.c = c #🐿️ 普通方法 def test1(self): print('a normal func') #🐿️ 静态方法 @staticmethod def static_test(): print("a static class") #🐿️ 类方法 @classmethod def class_test(self): print("a class func") #🌾:类的__dict__属性 print(TestName.__dict__) #🌾:对象的__dict__属性 myTestName = TestName('c') print(myTestName.__dict__)
结果:
{ '__module__': '__main__', '__firstlineno__': 1, 'a': 2, 'b': 2, '__init__': <function TestName.__init__ at 0x1010f7560>, 'test1': <function TestName.test1 at 0x1010f7600>, 'static_test': <staticmethod(<function TestName.static_test at 0x1010f76a0>)>, 'class_test': <classmethod(<function TestName.class_test at 0x1010f7740>)>, '__static_attributes__': ('a', 'b', 'c'), '__dict__': <attribute '__dict__' of 'TestName' objects>, '__weakref__': <attribute '__weakref__' of 'TestName' objects>, '__doc__': None } {'a': 0, 'b': 1, 'c': 'c'}
🔊:利用__dict__可以给运行中的对象添加新的属性,接上例
myTestName.__dict__['new'] =10
结果:
{'a': 0, 'b': 1, 'c': 2, 'new': 10}
2. __name__的用法
在很多 Python程序里,经常会看到这样的一段代码:
if __name__ == '__main__': main()
但很多人对这个内置变量的具体含义并不清楚。作为 Python 的内置变量,__name__变量还是挺特殊的。它是每个 Python 模块必备的属性,但它的值取决于你是如何执行这段代码的。
在许多情况下,你的代码不可能全部都放在同一个文件里,或者你在这个文件里写的函数,在其他地方也可以用到。为了更高效地重用这些代码,你需要在 Python 程序中导入来自其他文件的代码。所以,在__name__ 变量的帮助下,你可以判断出这时代码是被直接运行,还是被导入到其他程序中去了。
- 当你直接执行一段脚本的时候,这段脚本的__name__ 变量等于__main__;
- 当这段脚本被导入其他程序的时候__name__ 变量等于脚本本身的名字;
🌰 示例1
新建一个名叫 nameScript.py 脚本文件
#🐿️ 自定义函数 def myFunction(): print('变量 __name__ 的值是 ' + __name__) #🐿️ 自定义函数,调用myFunction() def main(): myFunction() #🐿️ 结果 if __name__ == '__main__': main()
运行结果:
变量 __name__ 的值是 __main__
在所有其他代码执行之前__name__变量就被设置为__main__了。在此之后,通过执行 def 语句,函数 main() 和 myFunction() 的本体被载入。接着,因为这个 if 语句后面的表达式为真 true,函数 main() 就被调用了。而 main() 函数又调用了myFunction(),打印出变量的值__main__ 。
🌰 示例2
新建一个 importingScript.py 文件,将 nameScript.py 作为一个模组导入,重用这个 myFunction() 函数:
#🌾:nameScript.py内容 import index1 as ns #🌾:调用 ns.myFunction()
运行结果:
变量 __name__ 的值是 nameScript
🤔:为什么会出现这个结果?
变量的值变成了 nameScript,也就是我们导入的模块的名称。这是因为有了两个不同的作用域:一个是 importingScript 的,一个是 nameScript 的:
在 importingScript.py 里 __name__ 变量就被设置为 main。当导入 nameScript 的时候,Python 就在本地和环境变量 PATH 指向的路径中寻找对应名称的 .py 文件,
找到之后,将会运行导入的文件中的代码。
但这一次,在导入的时候,它自身的__name__ 变量就被设置为了 ‘nameScript’,接下来还是一样,函数 main() 和 myFunction() 的本体被载入。然而,这一次 if 语句后面的表达式结果为假 false,所以 main() 函数没有被调用。
导入完毕之后,回到 importingScript.py 中。现在 nameScript 模块中的函数定义已经被导入到当前的作用域中,于是我们通过 ns.myFunction() 的方式调用模块中的函数,
这个函数返回的是模块内的变量的值 ‘nameScript’。
但是如果在 importingScript 中打印 __name__ 变量的值,那直接执行 importingScript 的时候,它也会输出 __main__。原因在于,这个变量是在 importingScript 的作用域中的,如果没有特殊设定,默认为__main__。
#nameScript.py内容 import nameScript as ns #🌾:调用 ns.myFunction() #🌾:输出 print(__name__)
运行结果:
变量 __name__ 的值是 nameScript __main__
3. __file__的用法
内置变量__file__比较容易理解,它表示显示文件当前的位置。这里的文件当前位置值得是执行这个脚本时,脚本所在路径相对于执行路径所在的路径。比如有一个脚本test.py在路径/home/user/test/下,如果我们在/home/user/test/下执行:
#🌾:输出 print(__file__)
运行结果:
test.py
如果我们在/home/user下执行:
#🌾:输出 print(__file__)
运行结果:
test/test.py
!!!这里要注意,如果执行命令时使用绝对路径,__file__就是脚本的绝对路径。如果使用的是相对路径,__file__就是脚本的相对路径。
二、什么是内置类方法
1. __ new__ 和 __init__
new 和 init 这两个方法很容易混淆,平时定义类时,通常使用的都是init方法,很少用到new方法,但他们是有着截然不同的功能的。
__new__ : 在___init__触发前,自动触发。调用该类时,内部会通过__new__产生一个新对象
__init__ : 在调用类时自动触发。通过产生的对象自动调用__init__()
#🐿️ 定义类 class Demo(object): #🌾:条件: __new__: 在__init__触发前,自动触发。 def __new__(cls, *args, **kwargs): print('此处是__new__方法的执行') # python内部通过object调用内部的__new__实现产生一个空的对象 ---> 内存地址 return object.__new__(cls, *args, **kwargs) #🌾:条件: __init__: 在调用类时自动触发。 def __init__(self): print('此处是__init__方法的执行') #🐿️ 调用 demo = Demo() #🐿️ 输出 # 此处是__new__方法的执行 # 此处是__init__方法的执行
如果我们在类的new方法中,返回成其他类型对下,则最终得到的会是新类型。
2. __ getattr __ 和 __ getattribute __
🌾 __ getattr __ :在“对象.属性”获取属性时,若属性没有时触发。
#🐿️ 定义类 class Demo(object): # x = 10 def __getattr__(self,name): print('此处是__getattr__方法的执行') print(name) return 123 #想要返回的值 #🐿️ 创建实例对象 demo = Demo() #🐿️ 调用 print(demo.x) ''' 此处是__getattr__方法的执行 x 123 '''
🌾 __ getattribute __ :在 “对象.属性” 获取属性时,无论 "属性有没有" 都会触发。
#🐿️ 定义类 class Demo(object): # x = 10 #🌾 在 “对象.属性” 获取属性时,无论 "属性有没有" 都会触发 def __getattribute__(self, name): print('此处是__getattribute__方法的执行') # 注意: 此处不能通过对象.属性,否则会产生递归调用,程序崩溃 # return self.__dict__[item] print(name, '<-----打印属性名字') # raise AttributeError('抛出异常了') #🐿️ 创建实例对象 demo = Demo() #🐿️ 调用 print(demo.x) ''' 此处是__getattribute__方法的执行 x <-----打印属性名字 None '''
!!!注意 : 只要 __getattr__ 与 __getattribute__ 同时存在类的内部,只会触发 __getattribute__。
#🐿️ 定义类 class Demo(object): # x = 10 #🌾 在“对象.属性”获取属性时,若属性没有时触发 def __getattr__(self,name): print('此处是__getattr__方法的执行') print(name) return 123 #想要返回的值 #🌾 在 “对象.属性” 获取属性时,无论 "属性有没有" 都会触发 def __getattribute__(self, name): print('此处是__getattribute__方法的执行') # 注意: 此处不能通过对象.属性,否则会产生递归调用,程序崩溃 # return self.__dict__[item] print(name, '<-----打印属性名字') # raise AttributeError('抛出异常了') #🐿️ 创建实例对象 demo = Demo() #🐿️ 调用 print(demo.x) ''' 此处是__getattribute__方法的执行 x <-----打印属性名字 None '''
若 执行到 __getattribute__ 发现 raise AttributeError('抛出异常了'),则再触发 __getattr__
#🐿️ 定义类 class Demo(object): # x = 10 #🌾 在“对象.属性”获取属性时,若属性没有时触发 def __getattr__(self,name): print('此处是__getattr__方法的执行') print(name) return 123 #想要返回的值 #🌾 在 “对象.属性” 获取属性时,无论 "属性有没有" 都会触发 def __getattribute__(self, name): print('此处是__getattribute__方法的执行') # 注意: 此处不能通过对象.属性,否则会产生递归调用,程序崩溃 # return self.__dict__[item] print(name, '<-----打印属性名字') raise AttributeError('抛出异常了') #🐿️ 创建实例对象 demo = Demo() #🐿️ 调用 print(demo.x) ''' 此处是__getattribute__方法的执行 x <-----打印属性名字 此处是__getattr__方法的执行 x 123 '''
3. __setattr__ 和 __delattr__
🌾 __setattr__: 当 “对象.属性 = 属性值” , 添加或修改属性时触发执行。
#🐿️ 定义类 class Demo: #🌾 key---> 对象.属性名 value ---》 属性值 def __setattr__(self, key, value): print('此处是__setattr__方法的执行') print(key, value) #🌾 出现递归 # self.key = value # 此处是对 对象的名称空间 ---》 字典进行操作 self.__dict__[key] = value #🐿️ 调用 obj1 = Demo() obj1.x = 10 #🌾 原来设置属性时,会自动触发父类中的__setattr__,内部为对象添加x属性,值为20 #🐿️ 执行结果: print(obj1.__dict__) ''' 此处是__setattr__方法的执行 x 10 {'x': 10} '''
🌾 __delattr__: 当删除 “对象.属性” 属性的时候会触发执行。
#🐿️ 定义类 class Demo: x = 1 #🌾 初始化方法 def __init__(self,y): self.y = y #🌾 重写内置类函数 def __delattr__(self, item): print('此处是__delattr__方法的执行') #🐿️ 调用 obj1 = Demo(10) del obj1.y del obj1.x #🐿️ 执行结果: # 此处是__delattr__方法的执行 # 此处是__delattr__方法的执行
!!!注意:因为我们重写了 __delattr__ 方法,重复的 __delattr__ 方法里并没有实际执行,删除操作。 所以再此调用时,obj1.x, obj1.y 的值仍然存在,且为 1,10。
4. __str__
🤔:想一想下列情况为什么不一样?
#🐿️ 列表 l = list('hello') #l是list类的实例化出来的一个实例,即一个对象 print(l) #['h','e','l','l','o'] #🐿️ 对象 class Foo: pass f1 = Foo() print(f1) #<__main__.Foo object at 0x0000022735920688>
🌾 __str__:在打印对象时触发。
#🐿️:定义类 class Demo: def __str__(self): print('此处是__str__方法的执行') return '自定制的对象的显示方式' #🐿️:实例化 obj1 =Demo() #🐿️:调用 print(obj1) #【执行结果】: #此处是__str__方法的执行 #自定制的对象的显示方式
🔊:注意该方法必须要有一个 “字符串” 返回值。
5. __getitem__ 、__setitem__ 和 __delitem__
🌾 __getitem__:在对象通过 “对象[key]” 获取属性时触发。
#🐿️ 定义类 class Demo: def __init__(self,name): self.name = name def __getitem__(self, item): print('此处是__getitem__方法的执行') print(item) return self.__dict__[item] #🐿️ 定义类对象 obj1 = Demo('lili') #🐿️ 输出 print(obj1, '<----- 打印的对象') print(obj1['name']) #🐿️ 结果 ''' 此处是__getitem__方法的执行 name lili '''
🌾 __setitem__:在对象通过 “对象[key]=value值” 设置属性时触发。
#🐿️ 定义类 class Demo: def __setitem__(self, key, value): print('此处是__setitem__方法的执行') self.__dict__[key] = value
#🐿️ 定义对象 obj1 = Demo() print(obj1.__dict__) #赋值 obj1['name'] = 'baohan' print(obj1.__dict__)
#🐿️ 执行结果: ''' {} 此处是__setitem__方法的执行 {'name': 'baohan'} '''
🌾 __delitem__:在对象通过 del “对象[key]” 属性时触发。
#🐿️ 定义 class Demo: def __setitem__(self, key, value): print('此处是__setitem__方法的执行') self.__dict__[key] = value def __delitem__(self, key): print('此处是__delitem__方法的执行') self.__dict__.pop(key) #🐿️ 调用 obj1 = Demo() print(obj1.__dict__) obj1['name'] = 'baohan' print(obj1.__dict__) #del obj1.name del obj1['name'] print(obj1.__dict__) #🐿️ 执行结果: ''' {} 此处是__setitem__方法的执行 {'name': 'baohan'} 此处是__delitem__方法的执行 {} '''
6. __call__
🌾 __call__:在调用对象 “对象 + ()” 时触发。
#🐿️ 定义 class Demo: def __call__(self, *args, **kwargs): print('此处是__call__方法的执行') # 调用对象时返回的值 return [1,2,3,4] #🐿️ 调用 obj1 =Demo() obj1() #🐿️ 执行结果: # 此处是__call__方法的执行