【问题标题】:How to simplify this code by using properties?如何通过使用属性来简化此代码?
【发布时间】:2018-02-08 17:14:37
【问题描述】:

我阅读了文档,但不确定如何使用 Python 属性简化以下代码:

class PatientRecordJson:
    def __init__(self):
        self.json = {}

    def set_raw_data_field(self, string):
        self.json['raw_data'] = string

    def get_raw_data_field(self):
        return self.json.get('raw_data', None)

    def set_data_type(self, string):
        self.json['data_type'] = string

    def get_data_type(self):
        return self.json.get('data_type', None)

    def set_type_of_record(self, string):
        self.json['type_of_record'] = string

    def get_type_of_record(self):
        return self.json.get('type_of_record', None)

    def set_npi(self, string):
        self.json['npi'] = string

    def get_npi(self):
        return self.json.get('npi', None)

【问题讨论】:

  • 题外话:您是否意识到您已将json 设为,而不是实例、变量?这意味着该类的所有实例都将共享它(我怀疑这不是你想要的,除非只有一个)。
  • 谢谢@martineau 我是python世界的新手。

标签: python python-3.x properties


【解决方案1】:

如果您只是在学习 Python,这可能太高级了——但是通过使用它,您可以通过使用元类(实例是其他类的类)在很大程度上自动化创建任意数量的类的过程。

虽然这样做需要一些重要的代码,但它使定义目标类变得非常简单。另外,作为奖励,我添加了可选的类型检查。

def typed_property(field_name, expected_type=None):
    """ Helper function which creates and returns a property with the given
        name with optional type-checking. Each property retrieves or stores
        values from/to an instance-defined "json" dictionary attribute.
    """
    @property
    def prop(self):
        return self.json[field_name]

    @prop.setter
    def prop(self, value):
        if expected_type and not isinstance(value, expected_type):
            raise TypeError('Only {} values may be assigned to {}'.format(
                                expected_type.__name__, field_name))
        self.json[field_name] = value

    return prop


class PatientRecordMeta(type):
    """ Metaclass to define properties based on a class-level defined "fields"
        dictionary.
    """
    def __new__(metaclass, classname, bases, classdict):
        cls = super().__new__(metaclass, classname, bases, classdict)
        fields = classdict.get('fields')
        if not fields or not isinstance(fields, dict):
            raise TypeError('Class {} did not define required "fields" '
                            'instance dictionary'.format(classname))

        # Create the properties.
        for field, expected_type in fields.items():
            setattr(cls, field, typed_property(field, expected_type))

        return cls

定义的元类使得创建具有完全所需属性的类变得非常容易:

class PatientRecordJson(metaclass=PatientRecordMeta):
    fields = {'raw_data': str,
              'data_type': str,
              'type_of_record': str,
              'npi': int}  # Note changed to "int" to test type-checking,

    def __init__(self):
        self.json = {}  # define required instance attribute

    # Other methods could be defined here, too, if desired.
    # ...


patient_rec = PatientRecordJson()

patient_rec.raw_data = 'something'
patient_rec.bogus = 'something else'  # OK, but not saved in "self.json" dict.

try:
    patient_rec.npi = 'spam'  # -> Should cause a TypeError
except TypeError:
    pass  # expected TypeError occurred
else:
    print('Error: a TypeError did not occur as expected')

patient_rec.npi = 42  # Integer value is OK.

patient_rec.json['raw_data'] = 'eggs'  # can still do this
print(patient_rec.raw_data)  # -> eggs
print(patient_rec.npi)  # -> 42
print(patient_rec.json)  # -> {'raw_data': 'something', 'npi': 42}

【讨论】:

    【解决方案2】:

    您可以覆盖__getattr____setattr__,当您使用obj.prop 访问属性时会调用它们。

    class PatientRecordJson:
        properties = ['raw_data', 'data_type', 'type_of_record', 'npi']
    
        def __init__(self):
            self.json = {}
    
        def __getattr__(self, name):
            if name in PatientRecordJson.properties:
                return self.json.get(name)
    
            return super().__getattr__(name)
    
        def __setattr__(self, name, value):
            if name in PatientRecordJson.properties:
                self.json[name] = value
    
            return super().__setattr__(name, value)
    

    使用示例:

    pr = PatientRecordJson()
    pr.raw_data              #=> None
    pr.raw_data = 'raw data'
    pr.raw_data              #=> 'raw data'
    pr.json                  #=> {'raw_data': 'raw data'}
    pr.z                     #=> AttributeError
    pr.z = 2
    pr.z                     #=> 2
    pr.json                  #=> {'raw_data': 'raw data'}
    

    注意:您已经在类上定义了json,如果您希望它成为实例变量,请在__init__ 中的self 上创建它。

    【讨论】:

    • +1 用于将静态变量用于属性。我想改变我的答案,但看到你很快提交了一个。
    • 你破坏了return super().__getattr__(self)。应该是return super().__getattr__(name)
    • 这不使用property...至少如果您理解它意味着内置装饰器使用描述符协议来实现它...
    【解决方案3】:

    您可以使用__getattr____setattr__ 将动态字段视为对象本身的属性,而不是内部json 对象的属性。

    class PatientRecordJson:
        def __init__(self):
            self.fields = ['raw_data', 'data_type', 'type_of_record', 'npi']
            self.json = {}
    
        def __getattr__(self, key):
            if key not in self.fields:
                raise AttributeError
            return self.json.get(key, None)
    
        def __setattr__(self, key, data):
            if key not in self.fields
                raise AttributeError
            self.json[key] = data
    

    上面的示例将允许您像这样访问属性。

    patient = PatientRecordJson()
    patient.data_type = 'something'
    patient.npi = 12345
    patient.raw_data = 'whatever you want here'
    print(patient.data_type) #  'something'
    print(patient.doesntexist) #  AttributeError
    patient.notinfields = True #  AttributeError
    

    【讨论】:

    • OP 询问如何简化代码,而不是如何使用属性装饰器。
    • JIm:OP 没有具体说属性 decorator,但他们确实明确询问了属性 - 请参阅问题的标题和第一行(也是唯一的)文本在里面。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-19
    • 1970-01-01
    • 2019-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多