首页 > 解决方案 > Python getter、setter 和操作字段

问题描述

我正在构建一个简单的类,它有一些只属于一个实例的变量,例如一个带有纪元时间戳的“创建”字段,以及在创建实例时设置的一些其他变量。作为练习,想以Pythonic 的方式来做这件事。此外,我想要一个将属性打印为 json 的函数,以便以后使用。到目前为止,我有这个:

import os
import uuid
import time


class Example:

    def __init__(self, data=None):
        self._data = data
        self._id = self.id
        self._environment = self.environment
        self._created = self.created

    @property
    def data(self):
        if self.environment == 'prd':
            self._data
        else:
            return None

    @data.setter
    def data(self, value):
        self._data = value

    @property
    def id(self):
        return str(uuid.uuid4())

    @id.setter
    def id(self, value):
        self._id = value

    @property
    def request(self):
        return self._request

    @request.setter
    def request(self, value):
        self._request = value

    @property
    def environment(self):
        return os.getenv('environment', 'dev')

    @environment.setter
    def environment(self, value):
        self._environment = value

    @property
    def created(self):
        return str(int(time.time()))

    @created.setter
    def created(self, value):
        self._created = value

    def to_json(self):
        return self.__dict__

输出:

from example import Example

e = Example()
print(e.to_json())

# {'_data': None, '_id': '5f946b10-0e89-4d65-9f76-b9f15a81197d', '_environment': 'dev', '_created': '1592835056'}

我有一些疑问:

  1. 操作属性的最佳位置是什么?例如,我在调用 self.created 时返回一个时间戳。我根据另一个属性操作数据属性。吸气剂是这种东西的正确位置吗?
  2. 我是否在init中正确地将私有变量分配给了自己?
  3. 我需要 json 中的所有变量以供以后处理,如何在没有下划线的情况下将它们输出到 json 中?
  4. 如何使用数据类完成同样的事情?

标签: pythongetter-setter

解决方案


关于您的问题 1. 是的,在普通 python 中,在读取或写入属性时执行代码的最佳位置是属性。或者,您可以使用所谓的描述符协议(高级)创建“类属性”对象。

对于您的问题 2。您的 init 不正确,因为您使用的某些变量在使用时未定义。此外created,environmentid属性也没有正确编写,因为如果您使用 setter,将写入一个新的私有属性(例如_id),但您没有在 getter 中使用它。使用属性的“好”方式是这样的:

class Example:
    def __init__(self):
        self._a = None

    @property
    def a(self):
        if self._a is None:
            return "my_default_val"
        else:
            return self._a

    @a.setter
    def a(self, value):
        self._a = value

e = Example()
print(e.a)
e.a = 2
print(e.a)

产量

my_default_val
2

对于您的问题 3,最适合您的是学习如何使用列表推导、字典推导等,并使用它来动态过滤您的vars(o)(或者o.__dict__,相同的)

现在参考您的问题 4。根据我的个人经验,我建议不要使用数据类,这只是attrs. 您可以使用attrs,或者似乎更适合对象可变的用例,pyfields. Indeedattrs还没有在属性更改时调用验证器/处理程序,并且强加了相当严格的开发理念(即,您不能将 anattr.ib任何类一起使用,因为该类__init__并且__setattr__attrs.

这是您可以使用的方法pyfields

import os
import uuid
import time

from pyfields import field, get_field_values, make_init


class Example:
    # the various fields
    _data = field(default=None)
    environment = field(default_factory=lambda o: os.getenv('environment', 'dev'))
    id = field(default_factory=lambda o: str(uuid.uuid4()))
    created = field(default_factory=lambda o: str(int(time.time())))
    request = field()

    # create a default constructor if you do not need a custom one
    __init__ = make_init()

    # 'data' is a dynamic view that depends on 'environment': still need a property 
    @property
    def data(self):
        if self.environment == 'prd':
            return self._data
        else:
            return None

    @data.setter
    def data(self, value):
        self._data = value

    def to_json(self):
        dct = get_field_values(self, public_only=True)
        dct['data'] = self.data
        return dct


e = Example(request='hello')
print(e.to_json())

产量

{'environment': 'dev', 'id': '8f3c2f8f-ce36-4e69-bfb9-b044db83be84', 'created': '1592897458', 'request': 'hello', 'data': None}

注意get_field_values不返回data属性的内容。请参阅此功能请求

如果需要,您可以进一步简化此示例,使用autofields它删除字段定义中的一些样板并在不存在时生成构造函数:

from pyfields import get_field_values, autofields

@autofields
class Example:
    # the various fields
    _data = None
    environment: str = os.getenv('environment', 'dev')
    id: str = str(uuid.uuid4())
    created: int = str(int(time.time()))
    request: str

    (... all the same than previously)

最后,如果您需要它们repr您可以使用. 它会自动检测类上有没有,你可以使用它的参数来自动创建它们:eqhashdictautoclassfieldsautofields

from autoclass import autoclass

@autoclass(autofields=True)
class Example:
    # the various fields
    _data = None
    environment: str = os.getenv('environment', 'dev')
    id: str = str(uuid.uuid4())
    created: int = str(int(time.time()))
    request: str

    # data is a dynamic view that depends on the environment: need a property
    @property
    def data(self):
        if self.environment == 'prd':
            return self._data
        else:
            return None

    @data.setter
    def data(self, value):
        self._data = value

    def to_json(self):
        dct = dict(self)  # <--- note this: autoclass dict behaviour
        dct['data'] = self.data
        return dct

e = Example(request='hello')
print(repr(e))

产量:

Example(environment='dev', id='ee56cb2f-8a4a-48ac-9789-956f1eaea132', created='1592901475', request='hello', 'data': None)

注意:我是pyfieldsand的作者autoclass;)


推荐阅读