【问题标题】:More Pythonic way of adding attributes to class?向类添加属性的更多 Pythonic 方式?
【发布时间】:2016-07-28 21:48:54
【问题描述】:

我正在处理来自两个不同网页的数据集,但对于同一个人 - 数据集是有关的法律信息。第一页上提供了一些数据,因此我使用正确的信息初始化被告对象,并将我目前没有数据的属性设置为null。这是课程:

class Defendant(object):
    """holds data for each individual defendant"""
    def __init__(self,full_name,first_name,last_name,type_of_appeal,county,case_number,date_of_filing,
                 race,sex,dc_number,hair_color,eye_color,height,weight,birth_date,initial_receipt_date,current_facility,current_custody,current_release_date,link_to_page):
        self.full_name = full_name
        self.first_name = first_name
        self.last_name = last_name
        self.type_of_appeal = type_of_appeal
        self.county = county
        self.case_number = case_number
        self.date_of_filing = date_of_filing
        self.race = 'null'
        self.sex = 'null'
        self.dc_number = 'null'
        self.hair_color = 'null'
        self.eye_color = 'null'
        self.height = 'null'
        self.weight = 'null'
        self.birth_date = 'null'
        self.initial_receipt_date = 'null'
        self.current_facility = 'null'
        self.current_custody = 'null'
        self.current_release_date = 'null'
        self.link_to_page = link_to_page

当我将一个填写一半的被告对象添加到被告名单中时,这就是它的样子:

list_of_defendants.append(Defendant(name_final,'null','null',type_of_appeal_final,county_parsed_final,case_number,date_of_filing,'null','null','null','null','null','null','null','null','null','null','null','null',link_to_page))

然后,当我从其他页面获取其余数据时,我将这些属性设置为 null,如下所示:

        for defendant in list_of_defendants:
            defendant.sex = location_of_sex_on_page
            defendant.first_name = location_of_first_name_on_page
            ## Etc.

我的问题是:当我只有一半想要存储的信息时,是否有更 Python 的方式来向类添加属性或初始化类对象的不那么丑陋的方式?

【问题讨论】:

  • 您可以将参数默认为'null',这样您就不需要在初始化时指定它们,您可以将最后一个指定为link_to_page = link_to_page并跳过其间的所有参数。
  • Null 值在 Python 中表示为 None,而不是字符串 'null'。请不要对Mr. Null进行无端指责。

标签: python python-2.7 oop


【解决方案1】:

首先,对您设置为 null 的任何参数使用默认值。这样,您甚至不需要在实例化对象时指定这些参数(并且您可以使用参数名称以任何顺序指定您需要的任何参数)。对于这些,您应该使用 Python 值 None 而不是字符串 "null",除非有特定原因需要使用该字符串。在 Python 2.x 中,具有默认值的参数需要放在最后,因此需要将 link_to_page 移到这些参数之前。

然后,您可以通过更新实例的__dict__ 属性来设置属性,该属性存储附加到实例的属性。每个参数都将被设置为具有相同名称的实例的属性。

def __init__(self, full_name, first_name, last_name, type_of_appeal, county, case_number, 
             date_of_filing, link_to_page, race=None, sex=None, dc_number=None,
             hair_color=None, eye_color=None, height=None, weight=None, birth_date=None,
             initial_receipt_date=None, current_facility=None, current_custody=None, 
             current_release_date=None):

      # set all arguments as attributes of this instance
      code     = self.__init__.__func__.func_code
      argnames = code.co_varnames[1:code.co_argcount]
      locs     = locals()
      self.__dict__.update((name, locs[name]) for name in argnames)

您还可以考虑从其他两个名称参数合成full_name。然后你不必传递冗余信息,它永远不会不匹配。您可以通过属性即时执行此操作:

@property
def full_name(self):
    return self.first_name + " " + self.last_name

为了更新,我会添加一个方法来做到这一点,但接受使用 ** 的仅关键字参数。为了帮助保护数据的完整性,我们将仅更改已存在并设置为 None 的属性。

def update(self, **kwargs):
    self.__dict__.update((k, kwargs[k]) for k in kwargs
                          if self.__dict__.get(k, False) is None)

然后您可以通过一次调用轻松更新所有您想要的:

defendant.update(eye_color="Brown", hair_color="Black", sex="Male")

为确保实例已完全填写,您可以添加一个方法或属性来检查以确保所有属性都不是None

@property
def valid(self):
    return all(self.__dict__[k] is not None for k in self.__dict__)

【讨论】:

    【解决方案2】:

    所以,一个更简单的例子来说明你可以怎么做:

    class Foo:
      def __init__(self, a, b, e, c=None, d=None):
        self.a = a
        self.b = b
        self.c = c
        self.d = d
        self.e = e
    

    但是,如果您从不在需要实例化时拥有cd,我会建议您改为:

    class Foo:
      def __init__(self, a, b, e):
        self.a = a
        self.b = b
        self.c = None
        self.d = None
        self.e = e
    

    编辑:另一种方法可能是:

    class Defendant(object):
        __attrs = (
            'full_name',
            'first_name',
            'last_name',
            'type_of_appeal',
            'county',
            'case_number',
            'date_of_filing',
            'race',
            'sex',
            'dc_number',
            'hair_color',
            'eye_color',
            'height',
            'weight',
            'birth_date',
            'initial_receipt_date',
            'current_facility',
            'current_custody',
            'current_release_date',
            'link_to_page'
        )
    
        def __update(self, *args, **kwargs):
            self.__dict__.update(dict(zip(self.__attrs, args)))
            self.__dict__.update(kwargs)
    
        def __init__(self, *args, **kwargs):
            self.__dict__ = dict.fromkeys(Defendant.__attrs, None)
            self.__update(*args, **kwargs)
    
        update_from_data = __update
    
    
    if __name__ == '__main__':
        test = Defendant('foo bar', 'foo', 'bar', height=180, weight=85)
        test.update_from_data('Superman', 'Clark', 'Kent', hair_color='red', county='SmallVille')
    

    【讨论】:

      【解决方案3】:

      我想说最pythonic的方式是这样的:

      class Defendant(Model):
          full_name = None  # Some default value
          first_name = None
          last_name = None
          type_of_appeal = None
          county = None
          case_number = None
          date_of_filing = None
          race = None
          sex = None
          dc_number = None
          hair_color = None
          eye_color = None
          height = None
          weight = None
          birth_date = None
          initial_receipt_date = None
          current_facility = None
          current_custody = None
          current_release_date = None
          link_to_page = None
      

      干净,一切都只定义一次并且自动运行。

      关于Model 超级类...如果您使用任何Web 框架,如Django,无论如何,从他们的模型继承,您就完成了。它拥有您需要的所有接线。

      否则,实现一些简短而甜蜜的简单方法,继承您的 Defendant 类:

      class Model(object):
          def __init__(self, **kwargs):
              for k, v in kwargs.items():
                  setattr(self, k, v)
      

      并根据您可用的字段进行实例化:

      d1 = Defendant(height=1.75)
      print d1.height
      
      d2 = Defendant(full_name='Peter')
      print d2.full_name
      

      您可以通过一些元编程实现更酷的事情,例如字段类型检查、值检查、重复声明等等。如果您使用的是 python 3,您可以轻松地将值传递给__init__通过 args(基于声明的顺序)或 kwargs 的方法。

      【讨论】:

        【解决方案4】:

        如果您可以将每个属性作为名称-值对传递,您可以使用类似:

        class Defendant(object):
            fields = ['full_name', 'first_name', 'last_name', 'type_of_appeal', 
                      'county', 'case_number', 'date_of_filing', 'race', 'sex',
                      'dc_number', 'hair_color', 'eye_color', 'height', 'weight', 
                      'birth_date', 'initial_receipt_date', 'current_facility', 
                      'current_custody', 'current_release_date', 'link_to_page']
        
            def __init__(self, **kwargs):
                self.update(**kwargs)
        
            def update(self, **kwargs):
                self.__dict__.update(kwargs)
        
            def blank_fields(self):
                return [field for field in self.fields if field not in self.__dict__]
        
            def verify(self):
                blanks = self.blank_fields()
                if blanks:
                    print 'The fields {} have not been set.'.format(', '.join(blanks))
                    return False
                return True
        

        用法如下:

        defendant = Defendant(full_name='John Doe', first_name='John', last_name='Doe')
        defendant.update(county='Here', height='5-11', birth_date='1000 BC')
        defendant.verify()
        # The fields type_of_appeal, case_number, date_of_filing, race... have not been set.
        

        将其扩展为使用必填字段和可选字段会很容易。或者,您可以在初始化中添加所需的参数。或者,您可以检查以确保每个名称-值对都有一个有效的名称。等等……

        【讨论】:

          猜你喜欢
          • 2012-10-10
          • 1970-01-01
          • 1970-01-01
          • 2015-01-16
          • 2022-12-04
          • 2016-03-03
          • 2018-10-06
          • 2015-06-16
          • 2012-08-17
          相关资源
          最近更新 更多