Python内置方法详解(下)
六、property属性
概述
property()函数是创建数据描述符的一种简洁方式,它允许在访问对象属性时自动触发函数调用。其函数签名如下:
property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
属性是一种特殊类型的描述符,用于处理对实例属性的所有访问操作。property()函数接受若干函数作为参数,这些参数实际上是类定义中的方法。当类被创建时,property()会被调用,而这些作为参数的方法是非绑定的函数。
定义方式
属性的定义有两种主要方式:
- 装饰器方式:在方法上应用@property装饰器
- 类属性方式:在类中定义值为property对象的类属性
经典类与新式类的区别
经典类(不继承object)只有一种@property装饰器,而新式类(继承object)支持三种装饰器:@property、@property名.setter、@property名.deleter。
经典类示例:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Product:
@property
def price(self):
return "test"
# 调用
obj = Product()
result = obj.price # 自动执行 @property 修饰的 price 方法
print(result)
执行结果:
test
新式类示例:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Product(object):
@property
def price(self):
print('执行获取方法')
@price.setter
def price(self, value):
print('执行设置方法')
@price.deleter
def price(self):
print('执行删除方法')
# 调用
obj = Product()
obj.price # 自动执行 @property 修饰的方法
obj.price = 123 # 自动执行 @price.setter 修饰的方法
del obj.price # 自动执行 @price.deleter 修饰的方法
执行结果:
执行获取方法
执行设置方法
执行删除方法
完整示例:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Product(object):
def __init__(self):
# 原价
self.original_price = 100
# 折扣
self.discount = 0.8
@property
def price(self):
# 实际价格 = 原价 * 折扣
new_price = self.original_price * self.discount
return new_price
@price.setter
def price(self, value):
self.original_price = value
@price.deleter
def price(self):
del self.original_price
obj = Product()
print(obj.price) # 获取商品价格
obj.price = 200 # 修改商品原价
执行结果:
80.0
类属性方式
使用类属性方式创建属性时,经典类和新式类没有区别:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Student(object):
def get_name(self):
return 'zhangsan'
NAME = property(get_name)
obj = Student()
result = obj.NAME # 自动调用get_name方法
print(result)
执行结果:
zhangsan
property构造方法的四个参数说明:
- 第一个参数是方法名,访问 属性 时自动触发执行
- 第二个参数是方法名,给 属性 赋值时自动触发执行
- 第三个参数是方法名,删除 属性 时自动触发执行
- 第四个参数是字符串,是该属性的描述信息
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Person:
def get_age(self):
return 25
def set_age(self, value):
return value + 10
def del_age(self):
return 'deleted'
AGE = property(get_age, set_age, del_age, '年龄描述信息')
obj = Person()
print(obj.AGE) # 自动调用第一个参数中定义的方法
obj.AGE = 30 # 自动调用第二个参数中定义的方法
print(obj.AGE) # 获取第四个参数中设置的值
执行结果:
25
40
实际应用案例
分页功能的实现:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Pager:
def __init__(self, current_page):
self.current_page = current_page
self.per_items = 10
@property
def start(self):
val = (self.current_page - 1) * self.per_items
return val
@property
def end(self):
val = self.current_page * self.per_items
return val
p = Pager(1)
print(p.start) # 起始值
print(p.end) # 结束值
执行结果:
0
10
案例:属性类型检查
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class User(object):
def __init__(self, name):
self.name = name # 实例化就触发property
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if not isinstance(value, str):
raise TypeError('必须是字符串类型')
self._name = value
@name.deleter
def name(self):
del self._name
user = User('admin')
print(user.name)
user.name = 'guest'
print(user.name)
七、__setitem__、__getitem__、__delitem__
方法说明
- __getitem__(self, key): 返回键对应的值,obj['key'] 时触发
- __setitem__(self, key, value): 设置给定键的值,obj['key'] = value 时触发
- __delitem__(self, key): 删除给定键对应的元素,del obj['key'] 时触发
示例一
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Cache(object):
def __getitem__(self, item):
print("__getitem__被调用,key=%s" % item)
def __setitem__(self, key, value):
print("__setitem__被调用,key=%s, value=%s" % (key, value))
def __delitem__(self, key):
print("__delitem__被调用,key=%s" % key)
obj = Cache()
result = obj['data1'] # 自动触发执行 __getitem__
obj['data2'] = 'hello' # 自动触发执行 __setitem__
del obj['data1'] # 自动触发执行 __delitem__
执行结果:
__getitem__被调用,key=data1
__setitem__被调用,key=data2, value=hello
__delitem__被调用,key=data1
示例二:通过字典方式操作实例属性
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class User(object):
def __init__(self, name):
self.name = name
def __getitem__(self, item):
return self.__dict__[item]
def __setitem__(self, key, value):
self.__dict__[key] = value
def __delitem__(self, key):
del self.__dict__[key]
u = User('tom')
u['age'] = 25
u['city'] = 'Beijing'
print(u.name)
print(u['age'])
print(u['city'])
print(u.__dict__)
执行结果:
tom
25
Beijing
{'name': 'tom', 'age': 25, 'city': 'Beijing'}
自定义列表类:让下标从1开始
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class MyList(list):
def __getitem__(self, offset):
return list.__getitem__(self, offset - 1)
if __name__ == "__main__":
alist = MyList([10, 20, 30])
print(alist[1]) # 返回列表的第0项,即10
print(alist[2]) # 返回列表的第1项,即20
print(alist[0]) # 返回最后一项,即30
执行结果:
10
20
30
八、__str__、__repr__、__format__
__str__和__repr__
__str__:控制print函数打印返回值,返回值必须是字符串类型
__repr__:控制返回值,返回值必须是字符串类型
当__str__未定义时,会使用__repr__来替代输出
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
def __repr__(self):
return 'Employee: %s, Salary: %d' % (self.name, self.salary)
emp = Employee('zhangsan', 8000)
print(emp) # 打印对象时调用 __repr__
print(str(emp)) # str函数调用 __str__,如果__str__未定义则调用__repr__
print(repr(emp)) # repr函数调用 __repr__
Time类格式化示例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Time(object):
def __init__(self, hour, minute):
self.hour = hour
self.minute = minute
def __str__(self):
return '%02d:%02d' % (self.hour, self.minute)
time1 = Time(9, 30)
time2 = Time(14, 45)
print(time1, time2)
执行结果:
09:30 14:45
__format__方法
__format__方法允许自定义对象的格式化输出:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
format_map = {
'ymd': '{0.year}-{0.month}-{0.day}',
'mdy': '{0.month}/{0.day}/{0.year}',
'dmy': '{0.day}.{0.month}.{0.year}'
}
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def __format__(self, format_spec):
if not format_spec or format_spec not in format_map:
format_spec = 'ymd'
return format_map[format_spec].format(self)
d1 = Date(2023, 8, 15)
print(format(d1, 'ymd'))
print(format(d1, 'mdy'))
print(format(d1, 'dmy'))
执行结果:
2023-8-15
8/15/2023
15.8.2023
九、__next__和__iter__实现迭代器协议
迭代器概念
在Python中,迭代有两个核心概念:
- Iterable(可迭代对象):实现了__iter__()方法的对象
- Iterator(迭代器):同时实现了__iter__()和__next__()方法的对象
迭代器协议的规则:
- __iter__方法返回迭代器本身
- __next__方法返回下一个迭代对象,迭代结束抛出StopIteration异常
迭代器实现示例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Counter:
def __init__(self, start, stop):
self.current = start
self.stop = stop
def __iter__(self):
return self
def __next__(self):
if self.current >= self.stop:
raise StopIteration
value = self.current
self.current += 1
return value
for i in Counter(1, 5):
print(i)
执行结果:
1
2
3
4
验证迭代器类型:
from collections import Iterable, Iterator
print(isinstance(Counter(1, 5), Iterator))
print(isinstance([1, 2, 3], Iterable))
斐波那契数列迭代器
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Fib:
def __init__(self):
self.a = 0
self.b = 1
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b, self.a + self.b
return self.a
f = Fib()
print(next(f))
print(next(f))
print(next(f))
for i in f:
if i > 100:
break
print(i, end=' ')
模拟range函数(带步长)
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Range:
def __init__(self, start, stop, step=1):
self.start = start
self.stop = stop
self.step = step
def __iter__(self):
return self
def __next__(self):
if self.start >= self.stop:
raise StopIteration
value = self.start
self.start += self.step
return value
for i in Range(0, 10, 2):
print(i, end=' ')
执行结果:
0 2 4 6 8
十、__doc__
__doc__属性用于存储类的描述信息:
class Animal:
'''这是一个动物类'''
pass
print(Animal.__doc__)
注意:__doc__属性无法被继承
class Animal:
'''动物基类'''
pass
class Dog(Animal):
pass
print(Dog.__doc__) # 输出 None
十一、__module__和__class__
- __module__:表示当前操作的对象所在的模块
- __class__:表示当前操作的对象的类
# lib/user.py
class User:
def __init__(self):
self.name = 'admin'
# index.py
from lib.user import User
obj = User()
print(obj.__module__) # 输出 lib.user
print(obj.__class__) # 输出 lib.user.User
class Person:
def __init__(self):
self.name = 'person'
obj = Person()
print(obj.__module__) # 在当前文件执行时输出 __main__
print(obj.__class__) # 输出 <class '__main__.Person'>
十二、__del__
__del__是析构方法,当对象在内存中被释放时自动触发执行。
如果产生的对象仅在Python程序级别(用户级),则无需定义__del__;如果对象需要管理系统资源(如打开文件、创建数据库连接),则必须在对象删除前回收系统资源。
class Database:
def __init__(self, name):
self.name = name
print('数据库 %s 连接成功' % name)
def __del__(self):
print('数据库 %s 连接已关闭' % self.name)
db = Database('mydb')
del db
print('后续代码继续执行')
执行结果:
数据库 mydb 连接成功
数据库 mydb 连接已关闭
后续代码继续执行
重要说明:
class FileHandler:
def __del__(self):
print('析构方法被调用')
f = FileHandler()
print('程序即将结束')
# 程序结束时Python会回收用户空间内存,然后自动调用__del__
典型应用场景:文件操作
# 正确方式
f = open('test.txt', 'r')
content = f.read()
f.close()
# 使用with语句(推荐)
with open('test.txt', 'r') as f:
content = f.read()
十三、__enter__和__exit__
上下文管理协议
为了使对象支持with语句,必须在类的__enter__和__exit__方法中实现相应逻辑。
class FileManager:
def __init__(self, filename):
self.filename = filename
def __enter__(self):
print('进入with语句块,__enter__被调用')
self.file = open(self.filename, 'w')
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
print('离开with语句块,__exit__被调用')
self.file.close()
return True
with FileManager('test.txt') as f:
f.write('Hello World')
print('正在写入文件')
执行结果:
进入with语句块,__enter__被调用
正在写入文件
离开with语句块,__exit__被调用
异常处理
__exit__方法的三个参数分别代表异常类型、异常值和追溯信息。如果__exit__返回True,异常会被吞掉,程序继续执行。
class Manager:
def __enter__(self):
print('enter')
def __exit__(self, exc_type, exc_val, exc_tb):
print('exc_type:', exc_type)
print('exc_val:', exc_val)
print('exc_tb:', exc_tb)
return True # 吞掉异常
with Manager() as m:
raise ValueError('发生错误了')
print('程序继续执行')
执行结果:
enter
exc_type: <class 'ValueError'>
exc_val: 发生错误了
exc_tb: ...
程序继续执行
with语句的用途:自动管理资源,无需手动干预清理工作。
十四、__call__
__call__方法允许对象像函数一样被调用。构造方法__init__的执行是由创建对象触发的,而__call__方法的执行是由对象后加括号触发的。
class Calculator:
def __init__(self, value):
self.value = value
def __call__(self):
return self.value * 2
calc = Calculator(10)
print(calc()) # 调用__call__方法
执行结果:
20
综合示例:__str__和__call__
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
def __str__(self):
return self.title
def __call__(self):
print('%s 的作者是 %s' % (self.title, self.author))
if __name__ == "__main__":
book = Book("Python编程", "Zhang San")
print(book) # 调用__str__方法
book() # 调用__call__方法
执行结果:
Python编程
Python编程 的作者是 Zhang San
十五、切片操作
Python 2中的切片方法
Python 2中使用__getslice__、__setslice__、__delslice__方法处理切片操作:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class MyCollection(object):
def __getslice__(self, i, j):
print('__getslice__被调用: i=%s, j=%s' % (i, j))
def __setslice__(self, i, j, sequence):
print('__setslice__被调用: i=%s, j=%s' % (i, j))
def __delslice__(self, i, j):
print('__delslice__被调用: i=%s, j=%s' % (i, j))
obj = MyCollection()
obj[-1:3]
obj[0:2] = [1, 2, 3]
del obj[0:3]
Python 3中的切片方法
Python 3统一使用__getitem__、__setitem__、__delitem__方法处理切片,通过slice对象传递参数:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class MyCollection(object):
def __getitem__(self, index):
if isinstance(index, slice):
print("获取切片: start=%s, stop=%s, step=%s" %
(index.start, index.stop, index.step))
def __setitem__(self, index, value):
if isinstance(index, slice):
print("设置切片: start=%s, stop=%s, step=%s" %
(index.start, index.stop, index.step))
print("设置的值:", value)
def __delitem__(self, index):
if isinstance(index, slice):
print("删除切片: start=%s, stop=%s, step=%s" %
(index.start, index.stop, index.step))
if __name__ == "__main__":
obj = MyCollection()
obj[-1:10]
obj[-1:10:1] = [1, 2, 3, 4]
del obj[-1:10:2]
执行结果:
获取切片: start=-1, stop=10, step=None
设置切片: start=-1, stop=10, step=1
设置的值: [1, 2, 3, 4]
删除切片: start=-1, stop=10, step=2
十六、算术运算符方法
__add__和__radd__
__add__:当类对象 + 其他对象时调用
__radd__:当其他对象 + 类对象时调用(左侧对象没有__add__方法时)
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Number:
def __init__(self, value):
self.value = value
def __add__(self, other):
return self.value + other
def __radd__(self, other):
return other + self.value
def __sub__(self, other):
return self.value - other
def __rsub__(self, other):
return other - self.value
num = Number(100)
print(num + 50) # 调用 __add__
print(50 + num) # 调用 __radd__
print(num - 30) # 调用 __sub__
print(30 - num) # 调用 __rsub__
执行结果:
150
150
70
-70
__iadd__方法
__iadd__用于实现 += 运算符:
class Counter:
def __init__(self, count=0):
self.count = count
def __iadd__(self, other):
self.count += other
return self
def __str__(self):
return str(self.count)
c = Counter(10)
c += 5
print(c) # 输出 15
以上内容涵盖了Python中常用的内置方法(魔术方法),理解这些方法能够帮助开发者更好地设计和实现自定义类,实现更丰富的功能。