一个在IT行业摸爬打滚的程序猿

0%

【Python】getattr和setattr的源码分析及使用

前言

鄙人有次在使用SQLALchemy的时候,对查询出来的对象,进行获取/重新赋值产生了点小问题

下面来一起看看这个小问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.dialects.mysql import TINYINT, INTEGER, VARCHAR, TEXT, DATE, DATETIME
from sqlalchemy import create_engine, Column

uri = "mysql+pymysql://root:@127.0.0.1:3306/test?charset=utf8mb4"
Base = declarative_base()
engine = create_engine(uri, pool_pre_ping=True, echo=True)
Session = sessionmaker(bind=engine)
Base = declarative_base()
session = Session()


class Student(Base):
__tablename__ = "tStudent"
id = Column(INTEGER, autoincrement=True, primary_key=True)
nValid = Column(INTEGER, comment="1-有效;2- 无效", nullable=False, default=1)
sName = Column(VARCHAR(100), comment="学生姓名", nullable=False)
nAge = Column(INTEGER, comment="年龄")
sCnDesc = Column(TEXT)
sEnDesc = Column(TEXT)

student_a = Student()
student_a.id = 23
student_a.nValid = 1
student_a.sName = "James"
student_a.nAge = 35
student_a.sCnDesc = "大家好,我叫詹姆斯!"
student_a.sEnDesc = "Hello everyone, My name is James!"

session.add(student)
session.commit()

鄙人开发中遇到一个这样的使用:
当我通过sqlalchemy查询数据,而且是使用.all()来查询的时候,
返回的是一个list,且里面的每一个元素都是一个Object;

而此时我想要预先设置好一个list/set来存这些字段,
然后获取的时候通过遍历来获取,心里想:这样不就是解决我的问题了吗,这能有多难?

然后操作就是这样写:

1
2
3
4
5
6
res = session.query(Student).all()
# 比如,要获取属性id、sName、nAge:
keys = ["id", "sName", "nAge"]
for one in res:
for k in keys:
print(f"属性:{k} 对应的值为:{one.k}")

现在请你们来猜测一下这个使用对不对?或者输出结果是什么?你们先想一下结果再往下看…

此时运行将会得到下面的错误,是不是很惊喜?嗯哼?啥玩意啊?

1
2
3
4
Traceback (most recent call last):
File "E:/projects/python/daily/temp.py", line 208, in <module>
print(f"属性:{k} 对应的值为:{one.k}")
AttributeError: 'Student' object has no attribute 'k'

看了一下,没有属性 k,这student对象的确没有属性 k啊,我的天,那我得怎么用?

然后这时候如果你使用 getattr的话,这就很容易解决你这种需求,下面来分析一下源码:
这两个作为Python的内置属性,下面来分析一下源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def getattr(object, name, default=None): # known special case of getattr
"""
getattr(object, name[, default]) -> value

Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.
When a default argument is given, it is returned when the attribute doesn't
exist; without it, an exception is raised in that case.
"""
pass


def setattr(x, y, v): # real signature unknown; restored from __doc__
"""
Sets the named attribute on the given object to the specified value.

setattr(x, 'y', v) is equivalent to ``x.y = v''
"""
pass

从上面的源码可以分析出来 getattr就是从一个对象从获取你想要的属性,使用格式是:

  • [x] getattr(对象, 你要获取的属性)

那么我这个例子里面的用法就是:

1
2
3
4
5
6
7
8
9
10
11
12
res = session.query(Student).all()
# 比如,要获取属性id、sName、nAge:
keys = ["id", "sName", "nAge"]
for one in res:
for k in keys:
# one.k 改为 getattr(one, k) 即可获取对应的属性
print(f"属性:{k} 对应的值为:{getattr(one, k)}")

>>>
属性:id 对应的值为:23
属性:sName 对应的值为:James
属性:nAge 对应的值为:35

  • [x] setattr的使用格式是:setattr(对象, 需要设置的属性, 设置的内容)
    请看下面栗子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
res = session.query(Student).all()
# 比如,要获取属性id、sName、nAge:
keys = ["id", "sName", "nAge"]
for one in res:
print("修改前:id:{} sName:{} nAge:{}".format(one.id, one.sName, one.nAge))
setattr(one, "id", 24)
setattr(one, "sName", "Kobe Bean Bryant")
setattr(one, "nAge", 42)
session.commit()
print("修改后:id:{} sName:{} nAge:{}".format(one.id, one.sName, one.nAge))
break

>>>
修改前:id:23 sName:James nAge:35
修改后:id:24 sName:Kobe Bean Bryant nAge:42

可以再次通过上面的getattr使用来查询一遍数据库,即可验证刚刚的用法对不对

==同时,官方还指出:setattr(x, 'y', v)是等价于x.y = v,笔者通过分析,这个用法其实也可以对这个x对象进行添加另外一个新的属性y==

下面这个例子是给对象增加一个school属性:

1
2
3
4
5
setattr(student_a, "school", "Beijing University")
print(student_a.schole)

>>>
Beijing University

小教程有用的话,点个赞吧
谢谢大家,我系渣渣辉

EOF