我有一个包含Tensor的类,Tensor有一系列不可预测的性质。
我想让它根据示例化时给出的列表生成一个带有getter和setter的属性列表。
你知道怎么做吗?
谢谢
class Items:
def __init___(
self,
names: List[str]
):
#: Property names to be generated
self.names: List[str] = names
#: Tensors in use
self.tensors: Dict[str,Tensor] = {}
def get_tensor(self, name:str) -> Tensor | None:
if name in self.tensors.keys():
return self.tensors[name]
return None
def set_tensor(self, name:str, tensor: Tensor):
self.tensors[name] = tensor
# The exact same setter and getter functions have to be generated for several properties:
# entries, classes, scores, target classes, target scores, embeddings, heatmaps, losses...
# The list is provided by self.names at instantiation
@property
def entries(self) -> Tensor | None:
return self.get_tensor("entries")
@entries.setter
def entries(self, tensor: Tensor):
self.set_tensor("entries", tensor)
2条答案
按热度按时间wfypjpf41#
我想到了两种方法。这两种方法都很混乱,而且不总是能很好地处理linters、mypy等。基本上,复制和粘贴或者仅仅使用你已经拥有的get和set方法可能是更干净、更清晰、更容易的解决方案。但是如果你要处理很多属性或者不同的类,那么这些方法应该可以工作。
选项1:使用getattr和setattr代替属性
选项2:在创建类时,使用元类创建所有你想要的属性,然后你可以为每一组属性名创建一个不同的类(或者继承一个新类)。
所以我不确定我是否真的会推荐这两种,但它们都是选择。
uurv41yg2#
有一个比上面详细的答案更简单的方法来做到这一点。
关于这段代码,只有几点需要注意。
首先,如果
Items
在将来被重构为通过元类接受关键字参数,那么您需要更改第7行,使用copy.deepcopy
从old_type
创建new_type
。另一个捕获是
name=name
部分,如果name
没有以这种方式绑定到<lambda>
作用域,则由于绑定到Items.__init__
的作用域,它将引用names
的最后一个元素。最后,在
get_tensor
中有一些冗余代码,这只是dict.get
的重新实现,我用后者替换了这些代码。它是如何工作的?
当Python在获取或设置属性的过程中查找一个属性时,它会检查
type(object)
对应的属性是否实现了描述符协议。property
实现了descriptor protocol,因此当Python查找属性时,我们可以使用getter和setter。那么
type
是如何工作的呢?在单参数type(object)
的情况下,它所做的只是检查对象的__class__
属性。__class__
本身只是对对象类的引用。因此,如果我们将__class__
属性更改为自己创建的类,我们就可以创建仅特定于示例的属性。最后,
type
的三参数版本可以用来创建新的类,传入名称、基和其他参数。如果您将来将此代码更改为使用元类,请将此行替换为new_type = copy.deepcopy(old_type)
。从这里开始,代码就变得相对简单了,我们使用
type
为示例创建一个新类,迭代名称并创建属性,最后将新类赋给__class__
属性。