Python 现代开发学习笔记

(三)实例属性与类属性

1. 每个实例自己的数据与类上共享的配置

# category 在类定义中建立;title 在每次初始化时写入对应实例。
class Book:
    category = "书籍"

    def __init__(self, title):
        self.title = title


first = Book("Python")
second = Book("NumPy")
print(first.title, second.title)
print(first.category, second.category)
Book.category = "学习资料"
print(first.category, second.category)

输出 Python NumPy、书籍 书籍、学习资料 学习资料。title 分别保存在两个实例上,category 保存在 Book 类上。实例读取这些普通属性时,自己没有对应名称,就继续到类及其基类查找;因此两个实例都能读到类上改后的配置。

2. 给实例赋值会形成自己的同名属性

# 第一次赋值只在 first 上建立 goal,不修改 Book.goal。
class Book:
    goal = 30


first = Book()
second = Book()
first.goal = 45
Book.goal = 60
print(first.goal, second.goal, Book.goal)
# 删除实例上的同名属性后,读取再次找到类上的值。
del first.goal
print(first.goal)

得到 45 60 60 和 60。first.goal = 45 使这次普通属性读取先找到实例值;Book.goal = 60 则重新绑定类上的值。两处写入的目标不同。

实例属性遮住类属性,再删除实例绑定

这里讲的是普通数据属性。后面的 property 会把读写交给专门的方法,属性访问还可以有自己的处理规则,不能把所有点号访问都理解成一次字典查找。

3. 类上的列表与实例上的独立列表

标签是每本书自己的数据。先观察把列表写在类上的结果:

# 观察示例:类只建立了一份 tags 列表,两个实例读取到同一个对象。
class Book:
    tags = []


first = Book()
second = Book()
first.tags.append("入门")
print(first.tags, second.tags)
print(first.tags is second.tags)

输出 ['入门'] ['入门'] 与 True。append 没有创建 first.tags 的新绑定,而是修改了从类上找到的共享列表。与上一章可变默认参数的问题一样,先找到共享对象,再修改它。

# 在每次 __init__ 中创建列表,每本书就各有一份标签。
class Book:
    def __init__(self, title):
        self.title = title
        self.tags = []


first = Book("Python")
second = Book("NumPy")
first.tags.append("入门")
print(first.tags, second.tags)
print(first.tags is second.tags)

这次是 ['入门'] [] 与 False。

共享的类列表与每次初始化建立的独立列表

对共享列表执行 first.tags += ["入门"] 也要分两步读:先对取到的列表原地扩展,再把结果写回实例属性。即使实例后来有了自己的 tags 绑定,类上的那份列表也已经被修改。需要独立数据时,从建立位置解决,放进 __init__,而不是依靠名称看起来属于哪个实例。

4. 按名称读取和更新属性

前面写 book.title,属性名直接出现在代码中。表单或配置有时会交来字符串 "title",可以让 getattr 按这个名称读取属性:

# getattr 按字符串查属性;setattr 按字符串完成一次属性赋值。
class Book:
    def __init__(self, title):
        self.title = title


book = Book("Python")
field = "title"
print(getattr(book, field))
setattr(book, field, "NumPy")
print(book.title)
# hasattr 检查能否读取指定属性;getattr 的第三个参数用于未找到时的返回值。
print(hasattr(book, "author"))
print(getattr(book, "author", "未填写"))

输出 Python、NumPy、False、未填写。getattr(book, "title") 沿着正常属性读取过程工作,setattr(book, "title", value) 对应属性赋值;后面定义 property 后,它们也会经过相应的读写处理。

第十三章按已提交字段名读取校验后的值,第十四章检查请求临时对象是否有某个名称,都沿用这里的操作。按名称访问属性

笔记正文与全部题解 · Ctrl / ⌘ K