按名称初始化、设置和获取我的自定义对象属性的 Pythonic 方法是啥?
Posted
技术标签:
【中文标题】按名称初始化、设置和获取我的自定义对象属性的 Pythonic 方法是啥?【英文标题】:What's the Pythonic way to initialize, set and get my custom object's attributes, by name?按名称初始化、设置和获取我的自定义对象属性的 Pythonic 方法是什么? 【发布时间】:2013-02-23 06:35:12 【问题描述】:我对 Python 很陌生,我需要声明自己的数据结构,不过我对如何做到这一点有点困惑。我目前有:
class Particle:
def __init__(self, mass, position, velocity, force):
self.mass = mass
self.position, self.velocity, self.force = position, velocity, force
def __getitem__(self, mass):
return self.mass
def __getitem__(self, position):
return self.position
def __getitem__(self, velocity):
return self.velocity
def __getitem__(self, force):
return self.force
但是,当我尝试使用以下方法定义类的实例时,这不起作用:
p1 = Particle(mass, position, velocity, force)
每个值都以 (0.0, 0.0) 结束(这是速度和力的值)。
谁能解释我哪里出错了,我需要从数据结构中提取数据,仅此而已。 (编辑:实际上,抱歉,我稍后将不得不更改它们)
谢谢
【问题讨论】:
这对我有用。 您所有的__getitem__
定义都是相同的方法。您需要检查实际检索的是哪个项目。
@askewchan 不应该。
另外:基于尺寸一致性,我会存储加速度而不是力。
如何尝试访问?按照您定义它的方式,p1[<anything>]
返回self.force
。我很好奇您如何想象访问不同的属性...
【参考方案1】:
首先,您应该了解__getitem__
是语法糖。拥有它很好,但如果你不需要它,就不要使用它。 __getitem__
和 __setitem__
基本上是如果您希望能够使用括号符号访问对象中的项目,例如:
p= Particle(foo)
bar = p[0]
如果您不需要这个,请不要担心。
现在,谈其他事情。看起来您在__init__
定义中具有您希望对象携带的主要特征,这很好。现在您需要使用self
将这些值实际绑定到您的对象上:
class Particle:
def __init__(self, mass, position, velocity, force):
self.mass = mass
self.position = position
self.velocity = velocity
self.force = force
原来如此。您现在可以使用点符号访问这些值,如下所示:
mass,pos,vel,f = 0,0,0,0 # just for readability
p = Particle(mass,pos,vel,f)
print p.mass, p.position, p.velocity, p.force
我们从中得到的好处之一是,如果我们问 python p
是什么,它会告诉你它是 Particle
类型的一个实例,如下所示:
in [1]: p
out[1]: <__main__.Particle instance at 0x03E1fE68>
理论上,当您使用这样的对象时,您希望在用户和数据之间有一个“抽象层”,这样他们就不会直接访问或操作数据。为此,您创建函数(就像您尝试使用 __getitem__
所做的那样)以通过类方法调解用户和数据之间的交互。这很好,但通常不是必需的。
在您更简单的情况下,要更新这些属性的值,您可以直接按照我们访问它们的方式进行操作,使用点符号:
in [2]: p.mass
out[2]: 0
in [3]: p.mass = 2
in [4]: p.mass
out[4]: 2
您可能已经明白这一点,但__init__
函数甚至class
定义(您通常会/应该定义类的大部分属性和方法)并没有什么神奇之处。某些类型的对象非常允许您随时随地添加属性。这可能很方便,但通常非常hacky并且不是好的做法。我不是建议你这样做,只是告诉你这是可能的。
in [5]: p.newattr ='foobar!'
in [6]: p.newattr
out[6]: 'foobar!'
很奇怪吧?如果这让你的皮肤爬行......好吧,也许它应该。但这是可能的,我是谁说你能做什么和不能做什么。所以这是对课程运作方式的一种体验。
【讨论】:
太好了,谢谢!快速跟进问题,创建 Particle 实例后如何更改值? @Incredidave 赋值,和变量一样(有一些细微的差别,但大多数人相处了几个月都没有理解它们,所以你应该没问题^^):p.mass = new value
@Incredidave 将帖子更新为演示作业并向您展示一点黑魔法【参考方案2】:
class Particle:
def __init__(self, mass, position, velocity, force):
self.mass = mass
self.position = position
self.velocity = velocity
self.force = force
particle = Particle(1, 2, 3, 4)
print(particle.mass) # 1
如果你想假装你的类有属性,你可以使用@property
装饰器:
class Particle:
def __init__(self, mass, position, velocity, force):
self.mass = mass
self.position = position
self.velocity = velocity
self.force = force
@property
def acceleration(self):
return self.force / self.mass
particle = Particle(2, 3, 3, 8)
print(particle.acceleration) # 4.0
【讨论】:
这太好了,谢谢。我对 Python 很陌生,我一直在努力寻找合适的东西来描述这一点。与 Python 一样,答案比我想象的要简单。 出于兴趣,为什么要假装拥有属性,而实际上拥有属性如此容易? @delnan 假设一个属性由依赖于其他两个属性的公式定义。如果您更改其中之一(如质量),您还必须更新加速度。这可能意味着错误或混乱的代码。 @WaleedKhan 我是一名 Python 资深人士。当我说“属性”时,我的意思是property
内置,最好用作装饰器。正如您可能知道或可能不知道的那样,它正是这样做的——只是以一种更好、更漂亮的方式。
@delnan 我将如何使用您所说的方法制作例如上述加速属性?【参考方案3】:
似乎collections.namedtuple
是你所追求的:
from collections import namedtuple
Particle = namedtuple('Particle', 'mass position velocity force')
p = Particle(1, 2, 3, 4)
print p.velocity
【讨论】:
仅当属性在对象的生命周期内不必更改,并且您可以将其作为一个序列时。当您需要添加方法或类似构造函数的东西时,namedtuples 的简洁性也会受到打击(它们仍然有用,但需要更仔细的处理和丑陋的代码才能使其正常工作)。 @delnan 是的,但我会按照 OP 的声明all I need from the data structure is to be able to pull the data out of it, nothing else.
是的,你的回答没问题。只是我几乎从未见过甚至暗示过这些限制,即使很重要,所以我养成了提及它们的习惯。
啊,废话,我会改变它。之后我需要对它们进行一些编辑(我的错!)。【参考方案4】:
你可以在使用它之前把这个类定义放在前面。如果要声明,请查看本站:http://www.diveintopython.net/getting_to_know_python/declaring_functions.html
顺便说一句,你的问题类似于这篇文章:Is it possible to forward-declare a function in Python? 和这篇文章:Is it possible to use functions before declaring their body in python?
【讨论】:
【参考方案5】:如果你只是需要存储一些属性值(类似于C语言struct
),你可以这样做:
class myContainer(object):
pass # Do nothing
myContainerObj = myContainer()
myContainerObj.storedAttrib = 5
print myContainerObj.storedAttrib
【讨论】:
不,这只是写字典的一种糟糕的方式。我反对在没有非常充分的理由的情况下使用向对象添加和删除属性的能力。它使错误更难被发现(有时这个东西有一个属性,有时它没有,取决于控制流)。 @delnan 我一般同意,但对于 Python 新手来说,这是一个简单的解决方案,不需要了解字典、散列和get()
方法。 (它也在 Python 教程文档中:docs.python.org/3/tutorial/classes.html#odds-and-ends)
edit: (insert: "get()
方法在属性不存在时指定一个值,或者其他方法来探测它是否已被定义")。就个人而言,我宁愿看到一个完整的类,其中包含 __init__
和所有属性的正确 get/set 方法,但在我(可能不正确)看来,这满足了 OP 的需求。)【参考方案6】:
在 Python 3.7+ 中有数据类库。这个库将允许您创建自己的类来使用装饰器快速保存数据,@dataclass
。
@dataclass
装饰器允许您快速定义功能并将其添加到您打算主要用于保存数据的类。
您的问题的数据类可能会如下实现。我已经包含了类型提示和默认值,您可能会觉得它们很有帮助。
from dataclasses import dataclass
@dataclass
class Particle:
mass: float
position: float
velocity: float = 0.0
force: float = 0.0
Here is a useful article 解释了如何在 Python 3.7+ 中使用数据类以及其他一些功能。
【讨论】:
以上是关于按名称初始化、设置和获取我的自定义对象属性的 Pythonic 方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
Magento 分层导航“您不能多次定义相关名称‘mycustomattribute’”