【问题标题】:Python getters, setters and manipulating fieldsPython getter、setter 和操作字段
【发布时间】:2020-06-22 14:15:08
【问题描述】:

我正在构建一个简单的类,它有一些只属于一个实例的变量,例如一个带有纪元时间戳的“created”字段,以及在创建实例时设置的一些其他变量。作为练习,希望以 most 可能的 Python 方式来完成此操作。此外,我想要一个将属性打印为 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. 如何使用数据类完成同样的事情?

【问题讨论】:

  • 您的代码不起作用。您缺少一些变量 (self.id & self.created)。你能解决这个问题吗?
  • 这也可能是在代码审查堆栈交换中提出的一个好问题。

标签: python getter-setter


【解决方案1】:

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

对于您的问题 2。您的 init 不正确,因为您使用的某些变量在使用时未定义。此外,createdenvironmentid 属性也未正确编写,因为如果您使用 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。事实上,attrs 还没有在属性更改时调用验证器/处理程序,并且强加了相当严格的开发理念(即,您不能将 attr.ibany 类一起使用,因为类 __init__ 和 @ 987654340@被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 属性的内容。见this feature request

如果需要,您可以进一步简化此示例,使用 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)

最后,如果您需要它们,您可以使用 autoclass 添加一个不错的 repreqhashdict 行为等。它会自动检测类上有fields,你可以使用它的autofields参数自动创建它们:

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)

注意:我是pyfieldsautoclass 的作者;)

【讨论】:

  • 这正是我想要的,一个非常彻底的解释!我在 Python 中进行了很多函数式编程,但还没有对类进行太多工作。
猜你喜欢
  • 2016-02-24
  • 1970-01-01
  • 2021-02-03
  • 2011-02-23
  • 2018-05-11
  • 2020-05-29
  • 2023-03-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多