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__

newinit 这两个方法很容易混淆,平时定义类时,通常使用的都是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__方法的执行

posted on 2024-12-19 14:55  梁飞宇  阅读(476)  评论(0)    收藏  举报