Python面向对象之内置方法
内置方法
【一】常用方法
-
特殊属性
-
特殊方法(魔法方法) :满足一定的条件就会自动触发
-
__new__
:创建实例时触发 -
__init__
:初始化对象 -
__str___
/__repr__
:print()/str()/repr()
时触发,必须返回字符串 -
__del__
:删除对象时触发 / 程序执行完毕时也会执行 -
__enter__
/__exit__
:上下文管理 -
__setattr__
/__delattr__
/__getattr__
和__getattribute__()
:特定情况时触发 -
__setitem__
/__getitem__
/__delitem__
:中括号取值时触发
-
【二】代码详细
官方文档
【1】特殊方法(魔法方法)
1.__new__
- 创建实例时调用
class Foo(object):
def __new__(cls, *args, **kwargs):
print("创建实例时被调用")
f = Foo() # 创建实例时被调用
2.__init__
- 初始化对象
class Foo(object):
def __init__(self, name,age):
self.name = name
self.age = age
print("初始化实例时被调用")
f = Foo('001',18) # 初始化实例时被调用
print(f.__dict__) # {'name': '001', 'age': 18}
3.__del__
- 删除对象时触发 / 程序执行完毕时也会执行
'''实例被销毁时触发'''
class Foo(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __del__(self):
print("实例被销毁时触发")
f = Foo('001', 18)
del f # 输出:实例被销毁时触发
'''程序结束时触发'''
class Foo(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __del__(self):
print("程序结束时触发")
f = Foo('001', 18)
print("====程序即将结束====")
'''
====程序即将结束====
程序结束时触发
'''
- 官方文档
__del__()
- 有点没理解【: (】,但是上述两种情况会触发
__del__
4.__call__
- 对象作为函数被”调用“时触发,如
对象+()
- 此方法会在实例作为一个函数被“调用”时被调用;如果定义了此方法,则
x(arg1, arg2, ...)
就大致可以被改写为type(x).__call__(x, arg1, ...)
。
class Foo(object):
def func(self):
print("func")
def __call__(self, *args, **kwargs):
print("call")
f = Foo()
f() # call
class Foo(object):
def func(self):
print("func")
def __call__(self, *args, **kwargs):
for i in args:
print(i, end='-')
# 可以传参并自行定制参数的后续执行
f = Foo()
f(1, 2, 3, 4) # 1-2-3-4-
5.__str___
/__repr__
print()/str()/repr()
时触发,必须返回字符串
'''__str__'''
class Foo(object):
def __str__(self):
print("print()时触发")
print("str()时触发")
return "必须要返回字符串"
# return 123 # 返回其他类型将会报错
# TypeError: __str__ returned non-string (type int)
f = Foo()
print(f) # print()时会自动打印出返回值
res = str(f) # str()时,不会打印返回值
print(res) # 可以打印结果
# 必须要返回字符串
'''__repr__'''
'''除调用方式略有差别,其余一致,返回值同样需要是字符串'''
class Foo:
def __repr__(self):
print("print() / repr()时触发")
return "repr"
f = Foo()
print(f)
repr(f)
'''
print() / repr()时触发
repr
print() / repr()时触发
'''
[5.1]__str__
优先级更高
class Foo(object):
def __repr__(self):
print("repr")
return "print() / repr()时触发"
def __str__(self):
print("str")
return "print() / str()时触发"
# def __repr__(self):
# print("repr")
# return "print() / repr()时触发"
f = Foo()
print(f)
# print()会同时触发str和repr
# 且与上下位置无关
'''
str
print() / str()时触发
'''
[5.2] 调用方式不一致
- 当
__str__
不存在时,会自动调用__repr__
- 内置类型
object
所定义的默认实现会调用object.__repr__()
。 - 又因为object是基类,所以默认情况下,都是调用
__repr__
class Foo(object):
def __repr__(self):
print("repr")
return "print() / repr()时触发"
f = Foo()
str(f) # repr
print(Foo()) # repr
- 当
__repr__
不存在时,不会自动调用__str__
class Foo(object):
def __str__(self):
print("str")
return "print() / str()时触发"
f = Foo()
print(repr(f)) # 未被调用
# 输出:<__main__.Foo object at 0x000001AAB3C99720>
print()
时,存在哪个调用哪个,如果都存在将只调用__str__
[5.3] __repr__
与__str__
的区别
【1】官方解释
-
object.__repr__(self)
- 由
repr()
内置函数调用以输出一个对象的“官方”字符串表示。如果可能,这应类似一个有效的 Python 表达式,能被用来重建具有相同取值的对象(只要有适当的环境)。如果这不可能,则应返回形式如<...some useful description...>
的字符串。返回值必须是一个字符串对象。如果一个类定义了__repr__()
但未定义__str__()
,则在需要该类的实例的“非正式”字符串表示时也会使用__repr__()
。此方法通常被用于调试,因此确保其表示的内容包含丰富信息且无歧义是很重要的。
- 由
-
object.__str__(self)
- 通过
str(object)
以及内置函数format()
和print()
调用以生成一个对象的“非正式”或格式良好的字符串表示。返回值必须为一个 字符串 对象。此方法与object.__repr__()
的不同点在于__str__()
并不预期返回一个有效的 Python 表达式:可以使用更方便或更准确的描述信息。内置类型object
所定义的默认实现会调用object.__repr__()
。
- 通过
【2】使用datetime.date
展示区别
import datetime
today = datetime.datetime.today()
print(str(today)) # 2024-01-16 15:55:22.397873
print(repr(today)) # datetime.datetime(2024, 1, 16, 15, 55, 22, 397873)
print(today.__str__()) # 2024-01-16 15:55:22.397873
print(today.__repr__()) # datetime.datetime(2024, 1, 16, 15, 55, 22, 397873)
__str__
的返回结果可读性强__repr__
的返回结果应更准确。
6.__setattr__
/__delattr__
/__getattr__
和__getattribute__()
-
__getattr__(self, item)
: 访问不存在的属性时调用 -
__setattr__(self, key, value)
:设置实例对象的一个新的属性时调用 -
__delattr__(self, item)
:删除一个实例对象的属性时调用
class Foo(object):
def __init__(self, name):
self.name = name
def __getattr__(self, item):
print(f"访问不存在的属性时触发")
return f"{item}不存在"
def __setattr__(self, key, value):
print("设置属性时触发")
# self.key = value # 将导致递归,需要使用__dict__字典改值 # self.key = value 相当于 obj.name = value 触发setattr
self.__dict__[key] = value
def __delattr__(self, item):
print("删除属性时触发")
# del self.item # 将导致递归,与setattr一个原因
self.__dict__.pop(item)
f = Foo('user') # 设置属性时触发 # 初始化时,也会触发setattr
print(f.__dict__) # {'name': 'user'}
print(f.sex)
'''
访问不存在的属性时触发
sex不存在
'''
f.name = '001'
print(f.__dict__) # {'name': '001'}
# 设置属性时触发
del f.name
# 删除属性时触发
print(f.__dict__) # {}
'''我们可以自行定制,对应操作时的结果'''
class Foo(object):
def __init__(self, name):
self.__dict__['name'] = name
def __getattr__(self, item):
# 当访问不存在的值时,自动创建
self.__dict__[item] = f'自动创建的{item}'
return self.__dict__[item]
def __setattr__(self, key, value):
# 将新创建的值改名存放
self.__dict__[f"{key}_new"] = value
def __delattr__(self, item):
# 将删除的值改名存放
old_value = self.__dict__[item]
self.__dict__[f"{item}_del"] = old_value
f = Foo('user')
print(f.__dict__) # {'name': 'user'}
print(f.sex) #自动创建的sex
f.name = '001'
print(f.__dict__) # {'name': 'user', 'sex': '自动创建的sex', 'name_new': '001'}
# 设置属性时触发
del f.name
# 删除属性时触发
print(f.__dict__) # {'name': 'user', 'sex': '自动创建的sex', 'name_new': '001', 'name_del': 'user'}
__getattribute__
- get | attribute :获取 | 属性/方法
getattribute
— Python 3.10.13 文档- 此方法会无条件地被调用以实现对类实例属性的访问。如果类还定义了
__getattr__()
,则后者不会被调用,除非__getattribute__()
显式地调用它或是引发了AttributeError
。此方法应当返回(找到的)属性值或是引发一个AttributeError
异常。为了避免此方法中的无限递归,其实现应该总是调用具有相同名称的基类方法来访问它所需要的任何属性,例如object.__getattribute__(self, name)
。
class Foo(object):
def __init__(self, name):
self.name = name
def __getattr__(self, item):
print(f"访问不存在的属性时触发")
return f"{item}不存在"
# 【1】返回属性值
def __getattribute__(self, item):
print("会无条件地被调用以实现对类实例属性的访问,不论是否存在")
return super().__getattribute__(item) # 必须返回找到的值,或者错误
# return getattr(self, item) # 相当于getattr(),但是这会触发递归
# return object.__getattribute__(itme) # 此语句同样可以实现,因为super()就是拿到object中的该方法
# 并且,由于,我在上述定义了__getattr__,所以当我获取不存在的属性时,调用__getattr__
f = Foo('user')
print(f.name) # 存在的值
print(f.sex) # 不存在的值
'''
print(f.name) >>>
会无条件地被调用以实现对类实例属性的访问,不论是否存在
user
print(f.sex) >>>
会无条件地被调用以实现对类实例属性的访问,不论是否存在
访问不存在的属性时触发
sex不存在
'''
8.__setitem__
/__getitem__
/__delitem__
getitem
— Python 3.10.13 文档- 通过中括号取值时,会自动触发
class Foo(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __getitem__(self, item):
return self.__dict__[item]
def __setitem__(self, key, value):
self.__dict__[key] = value
def __delitem__(self, key):
self.__dict__.pop(key)
f = Foo('user', 18)
# 触发getitem
print(f['name']) # user
# 触发setitem
f['name'] = 'learning'
print(f.__dict__) # {'name': 'learning', 'age': 18}
# 触发setitem
f['sex'] = 'male'
print(f.__dict__) # {'name': 'learning', 'age': 18, 'sex': 'male'}
# 触发delitem
del f['age']
print(f.__dict__) # {'name': 'learning', 'sex': 'male'}
- 同样的,我们也可以自行定制内容
class Foo(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __getitem__(self, item):
return f'{item}保密'
def __setitem__(self, key, value):
user_dict={key:value}
print(f"不准改!{user_dict}")
def __delitem__(self, key):
print(f"不要删【{key}】~球球了~")
f = Foo('user', 18)
# 触发getitem
print(f['name']) # name保密
# 触发setitem
f['name'] = 'learning' # 不准改!{'name': 'learning'}
# 触发setitem
f['sex'] = 'male' # 不准改!{'sex': 'male'}
# 触发delitem
del f['age'] # 不要删【age】~球球了~
9.__dir__
'''【->】表示函数的返回类型注解'''
class Foo(object):
def __dir__(self) -> [str]:
print("调用__dir__")
return ['a', 'b', 'c'] # 建议返回列表,列表中的元素为字符串
f = Foo() # 调用__dir__
print(dir(f)) # ['a', 'b', 'c']
class Foo(object):
def __dir__(self) -> [str]:
print("调用__dir__")
return super().__dir__() # 正常情况下,我们派生属性就好了,在原有的基础上加上我们自己的内容
f = Foo() # 调用__dir__
print(dir(f)) # [['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', ...]
10.__enter__
/__exit__
-
with方法中的必须包含的方法
-
object.__enter__(self)
进入与此对象相关的运行时上下文。
with
语句将会绑定这个方法的返回值到as
子句中指定的目标,如果有的话。 -
object.__exit__(self, exc_type, exc_value, traceback)
退出关联到此对象的运行时上下文。 各个参数描述了导致上下文退出的异常。 如果上下文是无异常地退出的,三个参数都将为
None
。如果提供了异常,并且希望方法屏蔽此异常(即避免其被传播),则应当返回真值。 否则的话,异常将在退出此方法时按正常流程处理。
[10.1] 代码演示
'''简单演示'''
class Foo(object):
def __init__(self, filename):
self.name = filename
def __enter__(self):
print("with语句一执行,就会执行enter中的代码")
def __exit__(self, exc_type, exc_val, exc_tb):
print("with语句执行完毕,就会执行exit中的代码")
f_obj = Foo('b.txt')
with f_obj as fp:
print("执行with语句中的代码")
'''
输出:
with语句一执行,就会执行enter中的代码
执行with语句中的代码
with语句执行完毕,就会执行exit中的代码
'''
'''使用with时,必须保证enter和exit都有,否则将报错'''
class Foo(object):
def __enter__(self):
print("with语句一执行,就会执行enter中的代码")
f_obj = Foo()
with f_obj as fp:
print("执行with语句中的代码")
# 输出: AttributeError: __exit__
[10.2] 使用enter和exit实现 with文件操作
'''使用enter和exit实现 with文件操作'''
class MyOpen(): # 继承open类,这样就可以调用原有的方法,且派生出自己的方法
def __init__(self, filename, mode='r', encoding='utf8'):
self.file = open(file=filename, mode=mode, encoding=encoding)
def __enter__(self):
print(type(self.file)) # <class '_io.TextIOWrapper'>
return self.file # 如果有返回值,将会赋值给as 后的变量
def __exit__(self, exc_type, exc_val, exc_tb):
print("开始执行关闭文件语句喽~")
self.file.close()
file = MyOpen('b.txt')
with file as f:
print(type(file)) # <class '__main__.MyOpen'>
data = f.read() # 调用read方法
print(data) # 2024-01-16 19:10:02 你好啊
# 最后输出:开始执行关闭文件语句喽~
[10.3] __exit__
参数介绍
-
__exit__(self, exc_type, exc_val, exc_tb)
-
exc_type:异常类型
-
exc_val:异常值
-
exc_tb:追溯信息
-
[10.3.1] with语句中出现异常
class Foo(object):
def __init__(self, filename):
self.name = filename
def __enter__(self):
print("with语句一执行,就会执行enter中的代码")
def __exit__(self, exc_type, exc_val, exc_tb):
print("with语句执行完毕,就会执行exit中的代码")
print(f"exc_type : {exc_type}")
print(f"exc_val : {exc_val}")
print(f"exc_tb : {exc_tb}")
f_obj = Foo('b.txt')
with f_obj as fp:
print("执行with语句中的代码")
raise AttributeError("手动创建的错误")
'''
输出:
with语句一执行,就会执行enter中的代码
执行with语句中的代码
with语句执行完毕,就会执行exit中的代码
exc_type : <class 'AttributeError'>
exc_val : 手动创建的错误
exc_tb : <traceback object at 0x0000015A39DADE00>
'''
[10.3.2]with语句中无异常
class Foo(object):
def __init__(self, filename):
self.name = filename
def __enter__(self):
print("with语句一执行,就会执行enter中的代码")
def __exit__(self, exc_type, exc_val, exc_tb):
print("with语句执行完毕,就会执行exit中的代码")
print(f"exc_type : {exc_type}")
print(f"exc_val : {exc_val}")
print(f"exc_tb : {exc_tb}")
f_obj = Foo('b.txt')
with f_obj as fp:
print("执行with语句中的代码")
# raise AttributeError("手动创建的错误")
# 如果无异常,那么三个参数将均为None
'''
输出:
with语句一执行,就会执行enter中的代码
执行with语句中的代码
with语句执行完毕,就会执行exit中的代码
exc_type : None
exc_val : None
exc_tb : None
'''
[10.3.3]有异常,但是希望方法屏蔽此异常(即避免其被传播)
class Foo(object):
def __init__(self, filename):
self.name = filename
def __enter__(self):
print("with语句一执行,就会执行enter中的代码")
def __exit__(self, exc_type, exc_val, exc_tb):
print("with语句执行完毕,就会执行exit中的代码")
print(f"exc_type : {exc_type}")
print(f"exc_val : {exc_val}")
print(f"exc_tb : {exc_tb}")
return True
f_obj = Foo('b.txt')
with f_obj as fp:
print("执行with语句中的代码")
raise AttributeError("手动创建的错误")
'''
输出:
with语句一执行,就会执行enter中的代码
执行with语句中的代码
with语句执行完毕,就会执行exit中的代码
exc_type : <class 'AttributeError'>
exc_val : 手动创建的错误
exc_tb : <traceback object at 0x000001A22D98DE00>
Process finished with exit code 0 # 将不会中断程序,而是正常执行完毕
'''
11.__gt__()
,__lt__()
,__eq__()
,__ne__()
,__ge__()
:“富比较”方法
- 富比较方法 — Python 3.10.13 文档
- 如果指定的参数对没有相应的实现,富比较方法可能会返回单例对象
NotImplemented
。按照惯例,成功的比较会返回False
或True
。不过实际上这些方法可以返回任意值,因此如果比较运算符是要用于布尔值判断(例如作为if
语句的条件),Python 会对返回值调用bool()
以确定结果为真还是假。
class Foo:
def __init__(self, num):
self.num = num
def __lt__(self, other):
'''x<y'''
return f" lt | x<y |{self.num < other.num}"
def __le__(self, other):
'''x<=y'''
return f" le | x<=y |{self.num <= other.num}"
def __eq__(self, other):
'''x == y'''
return f" eq | x==y |{self.num == other.num}"
def __ne__(self, other):
'''x!=y'''
return f" ne | x!=y |{self.num != other.num}"
def __gt__(self, other):
'''x>y'''
return f" gt | x>y |{self.num > other.num}"
def __ge__(self, other):
'''x>=y'''
return f" ge | x>=y |{self.num >= other.num}"
a = Foo(10)
b = Foo(5)
print(a < b) # lt | x<y |False
print(a <= b) # le | x<=y |False
print(a == b) # eq | x==y |False
print(a != b) # ne | x!=y |True
print(a > b) # gt | x>y |True
print(a >= b) # ge | x>=y |True
- 模拟数字类型
'''类似的数值运算方法'''
# 正负,取反、绝对值
class Point(object):
def __init__(self):
self.x = -1
self.y = 5
def __pos__(self):
pass
def __neg__(self):
pass
def __invert__(self):
pass
def __abs__(self):
pass
# 加减乘除、取余,指数
def __add__(self, other):
pass
def __sub__(self, other):
pass
def __divmod__(self, other):
pass
def __mul__(self, other):
pass
def __mod__(self, other):
pass
def __pow__(self, power, modulo=None):
pass
# 逻辑运算符
def __and__(self, other):
pass
def __or__(self, other):
pass
def __xor__(self, other):
pass
# 位移
def __lshift__(self, other):
pass
def __rshift__(self, other):
pass
# 赋值语句
def __iadd__(self, other):
pass
def __imul__(self, other):
pass
def __isub__(self, other):
pass
def __idiv__(self, other):
pass
def __imod__(self, other):
pass
def __ipow__(self, other):
pass
def __ilshift__(self, other):
pass
def __irshift__(self, other):
pass
【2】内置方法
属性 | 含意 | |
---|---|---|
__doc__ |
该函数的文档字符串,没有则为 None ;不会被子类继承。 |
可写 |
__name__ |
该函数的名称。 | 可写 |
__qualname__ |
该函数的 qualified name。3.3 新版功能. | 可写 |
__module__ |
该函数所属模块的名称,没有则为 None 。 |
可写 |
__defaults__ |
由具有默认值的参数的默认参数值组成的元组,如无任何参数具有默认值则为 None 。 |
可写 |
__code__ |
表示编译后的函数体的代码对象。 | 可写 |
__globals__ |
对存放该函数中全局变量的字典的引用 --- 函数所属模块的全局命名空间。 | 只读 |
__dict__ |
命名空间支持的函数属性。 | 可写 |
__closure__ |
None 或包含该函数可用变量的绑定的单元的元组。有关 cell_contents 属性的详情见下。 |
只读 |
__annotations__ |
包含形参标注的字典。 字典的键是形参名,而如果提供了 'return' 则是用于返回值标注。 有关如何使用此属性的更多信息,请参阅 对象注解属性的最佳实践。 |
可写 |
__kwdefaults__ |
仅包含关键字参数默认值的字典。 | 可写 |
__self__
:类实例对象本身
- 获取原始实例对象
class MyClass:
def my_method(self):
print("This is a method.")
# 创建实例
obj = MyClass()
# 获取方法对象
method_object = obj.my_method
# 获取方法所属的实例
bound_instance = method_object.__self__
# 验证实例
print(bound_instance is obj) # 输出 True,表示绑定的实例是 obj
bound_instance.my_method() # This is a method.
- 在这个例子中,
__self__
用于获取method_object
所属的实例,即obj
。这在某些场景下可能会有用,例如在装饰器中获取方法所属的对象。一般情况下,直接通过method_object()
调用方法就足够了,而不需要显式地访问__self__
。
__func___
:函数对象
- 这是在处理装饰器或其他需要访问原始函数的场景时可能会使用的一种技术。
class MyClass:
def my_method(self):
print("This is a method.")
# 创建实例
obj = MyClass()
# 获取方法对象
method_object = obj.my_method
# 获取方法所属的原始函数
original_function = method_object.__func__
# 调用原始函数
original_function(obj) # 这里将实例作为第一个参数传递
__doc__
:方法的文档
class Foo(object):
'''doc文档所处的位置'''
'''第二行就不是了'''
def __init__(self, name, age):
self.name = name
self.age = age
f = Foo('001', 18)
print(f.__doc__)
# 输出:doc文档所处的位置
__name__
:方法名称
class MyClass:
def my_method(self):
print("This is a method.")
# 创建实例
obj = MyClass()
print(obj.my_method.__name__) # my_method
__moudle__
:方法所属模块的名称,没有则为 None
class MyClass:
def my_method(self):
print("This is a method.")
# 创建实例
obj = MyClass()
print(obj.my_method.__module__)
# __main__ # 执行文件中执行的就是__main__
from test1 import obj_test1
print(obj_test1.__module__)
# test1 # 导入文件,就是模块名
__get__
/__set__
/__delete__
:描述器
-
官方文档
- 3.3.2.2 实现描述器— Python 3.10.13 文档
- 我要崩溃了【😦】看不懂啊
-
描述器的概念:
- 描述器就是具有“绑定行为”的对象属性,其属性访问已被描述器协议中的方法所重载:
__get__()
,__set__()
和__delete__()
。 如果一个对象定义了以上方法中的任意一个,它就被称为描述器。 - 属性访问的默认行为是从一个对象的字典中获取、设置或删除属性。例如,
a.x
的查找顺序会从a.__dict__['x']
开始,然后是type(a).__dict__['x']
,接下来依次查找type(a)
的上级基类,不包括元类。
- 描述器就是具有“绑定行为”的对象属性,其属性访问已被描述器协议中的方法所重载:
-
来自GPT的概述
-
描述器(Descriptor)是 Python 中一种强大的工具,用于自定义属性的访问、修改和删除行为。描述器是实现了
__get__
、__set__
和__delete__
中至少一个方法的对象。描述器可以用于自定义类的属性行为,允许你在属性被访问、设置或删除时执行特定的操作。常见的描述器包括属性(property)、方法(method)和类方法(classmethod)。
以下是描述器的基本特性:
__get__(self, instance, owner)
: 当属性被访问时调用的方法。instance
是实例对象,owner
是拥有属性的类。__set__(self, instance, value)
: 当属性被设置时调用的方法。instance
是实例对象,value
是设置的值。__delete__(self, instance)
: 当属性被删除时调用的方法。instance
是实例对象。
描述器的典型应用之一是创建属性的计算值,其中
__get__
方法计算属性的值,而__set__
方法可能用于防止对属性进行非法的赋值。
-
class DescriptorExample:
def __init__(self, name):
self._name = name
def __get__(self, instance, owner):
print(f"Getting value of {self._name}")
return instance.__dict__[self._name]
def __set__(self, instance, value):
print(f"Setting value of {self._name} to {value}")
instance.__dict__[self._name] = value
class MyClass:
descriptor_attr = DescriptorExample("descriptor_attr")
# 使用描述器
obj = MyClass()
obj.descriptor_attr = 42 # 触发 __set__ 方法
print(obj.descriptor_attr) # 触发 __get__ 方法
剩余方法可见官方文档
- 崩溃了,越来越看不懂,希望将来能看懂
- 9. 类 — Python 3.10.13 文档