使用函数初始化数据类实例



我正试图创建一个数据类,将所有相关数据存储在一个对象中。如何初始化一个数据类实例,其中的值是从数据类中的函数计算的,这些函数接受参数?

这就是我目前的处境:

@dataclass
class Person: 
def Name(self):
return f'My name is {self.name[0]} {self.name[1]}.'
def Age(self):
return f'I am {self.age} years old.'
name: field(default_factory=Name(self), init=True)
age: field(default_factory=Age(self), init=True)
person = Person(('John', 'Smith'), '100')
print(person)

电流输出:

Person(name=('John', 'Smith'), age='100')

这就是我试图实现的输出:

Person(name='My name is John Smith', age='I am 100 years old')

我试图在dataclass';中使用How引用"self";字段?以供本主题参考。

首先-这是相当微妙的-我注意到将dataclasses.field()作为类型注释是不起作用的。即name: field(...)无效。我可以假设你的意思是做name: str = field(...)。这里str是名称的类型注释。

但是,即使使用这种方法,您也会根据传递default_factory参数的方式遇到TypeError——您需要一个可调用的无参数,尽管我注意到这在本用例中似乎没有帮助。

我的印象是,单独使用dataclasses.field(...)是不可能实现您想要实现的,因为我相信文档表明default_factory需要是可调用的零参数。

例如,default_factory=list的工作原理是list()提供了一个无参数构造函数。

但是,请注意以下情况是不可能的:

field(default_factory = lambda world: f'hello {world}!')

dataclasses不会将world的值传递给default_factory函数,因此使用这种方法会遇到错误。

好消息是,在你的情况下,有一些不同的替代方案或选择可以考虑,我将在下面概述。

仅初始化变量

为了解决这个问题,一种选择是使用InitVarfield(init=False):的组合

from dataclasses import field, dataclass, InitVar

@dataclass
class Person:
in_name: InitVar[tuple[str, str]]
in_age: InitVar[str]
name: str = field(init=False)
age: str = field(init=False)
def __post_init__(self, in_name: tuple[str, str], in_age: str):
self.name = f'My name is {in_name[0]} {in_name[1]}.'
self.age = f'I am {in_age} years old.'

person = Person(('John', 'Smith'), '100')
print(person)

打印:

Person(name='My name is John Smith.', age='I am 100 years old.')

属性

另一种用法可能是使用数据类中的字段属性。在这种情况下,如所示,将值传递给构造函数方法(即tuplestr(,并且用于每个字段属性的@setter方法生成格式化的字符串,该字符串存储在私有属性中,例如self._name

请注意,由于dataclasses当前处理(或静默忽略(属性的方式,在构造函数中没有传递字段属性的默认值时,会出现未定义的行为。

为了解决这个问题,您可以使用元类,比如我在本文中概述的元类。

from dataclasses import field, dataclass

@dataclass
class Person:
name: tuple[str, str]
age: str
# added to silence any IDE warnings
_age: str = field(init=False, repr=False)
_name: str = field(init=False, repr=False)
@property
def name(self):
return self._name
@name.setter
def name(self, name: tuple[str, str]):
self._name = f'My name is {name[0]} {name[1]}.'
@property
def age(self):
return self._age
@age.setter
def age(self, age: str):
self._age = f'I am {age} years old.'

person = Person(('John', 'Smith'), '100')
print(person)
person.name = ('Betty', 'Johnson')
person.age = 150
print(person)
# note that a strange error is returned when no default value is passed for
# properties; you can use my gist to work around that.
# person = Person()

打印:

Person(name='My name is John Smith.', age='I am 100 years old.')
Person(name='My name is Betty Johnson.', age='I am 150 years old.')

描述符

最后一个选项是在Python中使用描述符,我很可能会推荐这个选项,因为它比属性更容易设置。

据我所知,与声明大量属性相比,描述符本质上是一种更容易的方法,尤其是在所述属性的目的或用法非常相似的情况下。

下面是一个名为FormatValue:的自定义描述符类的示例

from typing import Callable, Any

class FormatValue:
__slots__ = ('fmt', 'private_name', )
def __init__(self, fmt: Callable[[Any], str]):
self.fmt = fmt
def __set_name__(self, owner, name):
self.private_name = '_' + name
def __get__(self, obj, objtype=None):
value = getattr(obj, self.private_name)
return value
def __set__(self, obj, value):
setattr(obj, self.private_name, self.fmt(value))

它可以按如下方式使用,其工作原理与上面的示例相同,具有属性:

from dataclasses import dataclass

@dataclass
class Person:
name: 'tuple[str, str] | str' = FormatValue(lambda name: f'My name is {name[0]} {name[1]}.')
age: 'str | int' = FormatValue(lambda age: f'I am {age} years old.')

person = Person(('John', 'Smith'), '100')
print(person)
person.name = ('Betty', 'Johnson')
person.age = 150
print(person)

打印:

Person(name='My name is John Smith.', age='I am 100 years old.')
Person(name='My name is Betty Johnson.', age='I am 150 years old.')

相关内容

  • 没有找到相关文章

最新更新