Python: связывание / связывание переменных в словаре - PullRequest
1 голос
/ 28 июня 2009

первый пост, так что играйте хорошо!

У меня довольно простой вопрос о словарях Python. Я хотел бы иметь некоторое значение dict, которое обновляется при изменении другой переменной (или, по крайней мере, пересчитывается при следующем вызове) - например:

mass = 1.0
volume = 0.5
mydict = {'mass':mass,'volume':volume}
mydict['density'] = mydict['mass']/mydict['volume']

Так что в этом случае mydict ['density'] просто возвращает 2.0. Если я изменю mydict ['mass'] = 2.0, плотность не будет обновлена. Хорошо - я могу понять почему - плотность определяется значениями, когда они были переданы в декларацию. Поэтому я подумал, что, возможно, я мог бы подойти к этому с помощью лямбда-выражения, например (извинения за ужасный код!):

mydict['density_calc'] = lambda x,y: x/y
mydict['density'] = mydict['density_calc'](mydict['mass'],mydict['volume'])

Но опять же, это только возвращает исходную плотность, несмотря на изменение mydict ['mass']. В качестве последней попытки я попробовал это:

def density(mass,volume): return mass/volume
mydict['density_calc'] = lambda x,y: density(x,y)
mydict['density'] = mydict['density_calc'](mydict['mass'],mydict['volume'])

Опять не игра в кости. Это кажется очень простой проблемой, поэтому извиняюсь заранее, но если кто-нибудь сможет мне помочь, я буду очень признателен!

Приветствия

Dave

Ответы [ 8 ]

9 голосов
/ 28 июня 2009

Похоже на злоупотребление структурой словаря. Почему бы не создать простой класс?

class Entity(object):
    def __init__(self, mass, volume):
        self.mass = mass
        self.volume = volume
    def _density(self):
        return self.mass / self.volume
    density = property(_density)
6 голосов
/ 28 июня 2009

Лучше всего в этом случае создать класс для этого и использовать динамическое свойство. e.g.:


class Body(object):
    def __init__(self):
        self.mass=1.0
        self.volume=0.5

    @property
    def density(self):
        return self.mass/self.volume

Это даст вам свойства массы, объема и плотности, где плотность рассчитывается на основе двух других значений. например,


b=Body()
b.mass=1
b.volume=0.5
print b.density # should be 2

b.mass=2.0
print b.density # should be 4

Однако, если вы привыкли использовать словарь, вам, вероятно, следует расширить его и переопределить «магические» методы __getitem__ и __setitem__, чтобы определить, изменилась ли масса или когда происходит доступ к плотности, и пересчитать при необходимости.

4 голосов
/ 28 июня 2009

Класс сделает это лучше:

class Thing:
    def __init__(self, mass, volume):
        self._mass = mass
        self._volume = volume
        self._update_density()

    def _update_density(self):
        self._density = self._mass / self._volume

    def get_density(self):
        return self._density

    density = property(get_density)

    # You're unlikely to need these, but to demonstrate the point:
    def set_mass(self, mass):
        self._mass = mass
        self._update_density()

    def set_volume(self, volume):
        self._volume = volume
        self._update_density()

brick = Thing(mass=2, volume=0.8)
print brick.density
# Prints 2.5

brick.set_mass(4)
print brick.density
# Prints 5.0

Даю слово, что вы хотите, чтобы плотность обновлялась, когда вы устанавливаете другие значения - проще было бы просто вычислить ее на лету, когда ее попросят:

    def get_density(self):
        return self._mass / self._volume

Тогда вам вообще не понадобится _update_density().

3 голосов
/ 28 июня 2009

Проблема в том, что словари не являются подходящим инструментом для вашей проблемы. Ваш вопрос как "как я могу забить гвоздь с помощью отвертки"; -)

Словари должны отображать ключи на значения и не заботиться о других значениях в словаре. То, что вы хотите, это класс

class PhysicalObject:
    def __init__(self, mass, volume):
         self.mass = float(mass)
         self.volume = float(volume)
    def setMass(self, mass):
         self.mass = float(mass)
    def setVolume(self, volume):
         self.volume = float(volume)
    def getDensity(self):
         return self.mass/float(self.volume)

 v = PhysicalObject(1.0, 2.0)
 print v.getDensity() # prints 0.5
 v.setMass(2.0)
 v.setVolume(1.0)
 print v.getDensity() # prints 2.0

Этот пример пересчитывает плотность каждый раз, когда вы хотите получить ее, но вы также можете рассчитать ее в функциях setMass и setVolume.

3 голосов
/ 28 июня 2009

Короткий ответ: ты не можешь.

Присвоенное выражение оценивается строго , когда оно встречается, поэтому любые изменения в терминах позже не имеют значения.

Вы можете решить эту проблему с помощью пользовательского класса dict, который перезаписывает методы getitem и setitem и делает что-то особенное (т.е. вычисляет значение) для некоторых ключей (например, плотности и конечное число других).

class MyDict(dict):
    def __getitem__(self, key):
            if key == "density":
                    return self["mass"] / self["volume"]
            return dict.__getitem__(self, key)

d = MyDict()
d["mass"] = 2.0
d["volume"] = 4.0
d["density"]    # 0.5

Взгляните на это .

1 голос
/ 28 июня 2009

Это так просто:

>>> mydict = {'mass':100, 'volume': 2, 'density': lambda: mydict['mass']/mydict['volume']}
>>> mydict['density']()
50
>>> mydict['mass']=200
>>> mydict['density']()
100
>>> mydict['volume'] = 4
>>> mydict['density']()
50

Моя лямбда-ссылка ссылается на mydict, а затем получает новейшие данные, хранящиеся в mydict, поэтому я избавляюсь от проблемы передачи массы и объема функции каждый раз, когда я хочу вызвать ее, как в вашем решении:

def density(mass,volume): return mass/volume
mydict['density_calc'] = lambda x,y: density(x,y)
mydict['density'] = mydict['density_calc'](mydict['mass'],mydict['volume'])

Однако mydict ['плотность'] - это функция, а не само значение.

0 голосов
/ 28 сентября 2016

Самореферентный словарь будет почти соответствовать вашей второй попытке (7 лет назад) и будет хорошо играть с любым другим кодом, использующим этот словарь. Другими словами, он все равно будет выглядеть и вести себя как утка.

class MyDict(dict):
        def __getitem__(self,key):
            value=dict.__getitem__(self,key)        
            if callable(value):
                value=value(self)
            return value

mydict=MyDict({'mass':1.0,'volume':0.5})
mydict['density'] = lambda mydict: mydict['mass']/mydict['volume']

print(mydict['density'])

2,0

mydict['mass']=2.0
print(mydict['density'])

4,0

0 голосов
/ 28 июня 2009

Вот быстрый способ узнать, как создать подкласс dict, чтобы соответствовать вашим спецификациям, но, вероятно, не соответствовать спецификациям Python для словаря:

class MyDict(dict):
    def __getitem__(self, key):
        if key == 'density':
           return self['mass'] / self['volume']
        else:
            return dict.__getitem__(self,key)
    def keys(self):
        return ['density'] + dict.keys(self)

x = MyDict()
x['mass'] = 1.0
x['volume'] = 0.5

print x

print x.keys()

print x['density']

x['mass'] = 2.0

print x['density']

который печатает

{'volume': 0.5, 'mass': 1.0}
['density', 'volume', 'mass']
2.0
4.0

Но это не относится к dict.iterkeys (), между прочим.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...