Python 2.7:使用setattr设置所有args / kwargs属性是一种好习惯吗?

时间:2017-04-12 12:21:35

标签: python python-2.7 setattr

this回答中,显示了如何自动设置kwargs / args传递给类__init__的属性

例如,可以这样做:

class Employee(object):
    def __init__(self, *initial_data, **kwargs):
        # EDIT
        self._allowed_attrs = ['name', 'surname', 'salary', 'address', 'phone', 'mail']

        for dictionary in initial_data:
            for key in dictionary:   
                if key in self._allowed_attrs:  # EDIT
                    setattr(self, key, dictionary[key])
        for key in kwargs:
            if key in self._allowed_attrs:  # EDIT
                setattr(self, key, kwargs[key])

在我的情况下,我事先已经知道了我将要通过的论点,所以我认为这个解决方案只是为了减少重复和更短的代码。

这被认为是好习惯吗? 哪些是针对手动初始化每个属性的此解决方案的优缺点?还有其他更好的方法吗?

编辑:由于第一个评论/答案(正确)专注于清理参数或列出参数,我认为这可以在此框架中轻松解决。

4 个答案:

答案 0 :(得分:3)

  

问题:...重复性更低,代码更短

您的示例代码需要 9行和28个关键字

class Employee(object):
    def __init__(self, name, surname, salary, address, phone, mail):
        self.name = name
        self.surname = surename
        self.salary = salary
        self.address = address
        self.phone = phone
        self.mail = mail

此默认值需要 6行和19个关键字。 总结一下,您的示例需要更多而不是“更短代码”。 我在默认设置中看不到任何“重复...代码”,所有分配都是完成

比较这两行,做同样的事情。控制哪些args可以通过:

self._allowed_attrs = ['name', 'surname', 'salary', 'address', 'phone', 'mail']  

def __init__(self, name, surname, salary, address, phone, mail):

第二个需要更少的努力并且一起完成 不需要if key in self._allowed_attrs:,因为python为你做了。

在一个真实的项目中,我会使用类似的东西

class Employee(object):
    def __init__(self, person, salary=None):
        self.id = unique_id()
        self.person = person
        self.salary = salary

所有person相关数据都将在object person中汇总。

  

<强>结论
  对于您的示例class Employee,我绝不会使用(*args, **kwargs)   (*args, **kwargs)参数只有在无法预测传递了哪个args时才有用。

答案 1 :(得分:1)

这使得无法弄清楚班级期望什么样的论点。

如果有人(或者你在几个月内)想在他们的代码中创建Employee,他们会查看构造函数的参数,看看他们应该传递什么(可能是手工,或者是IDE自动显示它们)。除了隐藏此代码之外,您的代码几乎没有实现。

答案 2 :(得分:1)

事先讨论:Python decorator to automatically define __init__ variablesPython: Is it a good idea to dynamically create variables?

<强>优点:

  • 减少代码重复

<强>缺点:

答案 3 :(得分:0)

完全可以使用语言内省功能来减少重复和键入的必要性。

当然,最好是注意正确处理属性,甚至清理内容 - 所以最好的办法是为__init__方法设置一个装饰器,或者等同于将执行所需操作的基类__init__:检查传递的参数是否适用于特定类,然后使用setattr在实例中设置它们的值。

我认为不太神奇的方法是在类层次结构中使用约定来将所需参数声明为类属性。 这样,您可以使用这些类属性来记录期望的参数及其类型,并将__init__签名保留为*args, **kwargs并让您的基类init处理它们。

SQLAlchemy Base模型执行此操作 - 您将类属性指定为特殊的“检测属性”,并在__init__中调用时自动分配它们。

更简单的方法是:

_sentinel = object()

class Base(object):
    def __init__(self, *args, **kwargs):
        for attr_name, class_attr in self.__class__.__dict__.items():
            if isinstance(class_attr, type) and kwargs.get(attr_name, _sentinel) != _sentinel:
                attr_value = kwargs[attr_name]
                if not isinstance(attr_value, class_attr):
                    raise TypeError("Parameter {} is expected to be of type {}".format(attr_name, class_attr))
                setattr(self, attr_name, attr_value)


class Person(Base):
    name = str
    age = int
    phonenumber = Phone
    ...

这将要求将类的所有参数作为命名参数传递 - 但所有参数都将自动分配给实例属性,它可以工作,可记录且安全。如果你想变得更好,只需将一些花哨的描述符类定义为你的类attr值。