在构造时通过**kwargs设置类的属性

1 投票
3 回答
1873 浏览
提问于 2025-04-15 23:16

我是个Python新手,

现在我在使用SQLAlchemy,手头有这样的代码:

from __init__ import Base
from sqlalchemy.schema import Column, ForeignKey
from sqlalchemy.types import Integer, String
from sqlalchemy.orm import relationship

class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)
    username = Column(String, unique=True)
    email = Column(String)
    password = Column(String)
    salt = Column(String)
    openids = relationship("OpenID", backref="users")

User.__table__.create(checkfirst=True)

#snip definition of OpenID class

def create(**kwargs):
    user = User()
    if "username" in kwargs.keys():
        user.username = kwargs['username']
    if "email" in kwargs.keys():
        user.username = kwargs['email']
    if "password" in kwargs.keys():
        user.password = kwargs['password']

    return user

这段代码在 /db/users.py 文件里,所以使用起来像这样:

from db import users
new_user = users.create(username="Carson", password="1234")
new_user.email = "email@address.com"
users.add(new_user) #this function obviously not defined yet

不过在 create() 这个函数里的代码有点笨,我在想有没有更好的办法来处理,不用写一大堆if判断,而且如果以后添加了不在User对象里的键,它会出错。比如:

for attribute in kwargs.keys():
    if attribute in User:
        setattr(user, attribute, kwargs[attribute])
    else:
        raise Exception("blah")

这样的话,我可以把这部分放到一个单独的函数里(希望已经有类似的函数?),这样我就不用一次又一次地写if判断了,也能在不修改这段代码的情况下改变表的结构。

有什么建议吗?

3 个回答

1

如果你不需要处理继承的属性,

def create(**kwargs):
    keys_ok = set(User.__dict__)
    user = User()
    for k in kwargs:
        if k in keys_ok:
            setattr(user, k, kwargs[k])

如果你需要处理继承的属性,inspect.getmembers这个工具可以帮你(你可以设置一个自定义的条件,来排除那些名字以下划线开头的成员,或者其他你不想通过这种方式设置的成员)。

我还建议,如果set(kwargs) - set(keys_ok)不为空,最好给个警告——也就是说,如果传给create的一些命名参数不能在创建的实例中作为参数设置,那可不是好事...!-)

2

其实,声明性基类已经自动插入了你想要的那个构造函数,这在声明性模块的文档中有说明。所以你只需要写 User(username="Carson", password="1234") 就能实现你想要的功能,而如果你写 User(something_not_an_attribute='foo'),就会出现错误。

2

我的建议是不要再简化了。因为如果你随便给对象加属性,可能会影响到一些重要的结构。

我唯一会简化的就是在使用字典时,不用 .keys() 这个方法;因为在检查某个键是否存在和遍历字典的时候,已经默认使用了键。

...

再想想,你可以在类里面定义一个属性,专门用来存放已知的安全属性,然后在函数里检查这个属性,再用 setattr() 来给实例设置属性。

撰写回答