How do I use a keyword as a variable name?

10,764

Solution 1

As mentioned in the comments, from is a Python keyword so you can't use it as a variable name, or an attribute name. So you need to use an alternative name, and do a conversion when reading or writing the JSON data.

To do the output conversion you can supply a new encoder for json.dumps; you can do that by overriding the ExchangeRates.json method. To do the input conversion, override ExchangeRates.from_json.

The strategy is similar in both cases: we create a copy of the dictionary (so we don't mutate the original), then we create a new key with the desired name and value, then delete the old key.

Here's a quick demo, tested on Python 2.6 and 3.6:

import json

class PropertyEquality(object):
    def __eq__(self, other):
        return (isinstance(other, self.__class__) and self.__dict__ == other.__dict__)

    def __ne__(self, other):
        return not self.__eq__(other)

    def __repr__(self):
        return '%s(%s)' % (self.__class__.__name__, ', '.join(['%s=%s' % (k, v) for (k, v) in self.__dict__.items()]))

class JsonAware(PropertyEquality):
    def json(self):
        return json.dumps(self, cls=GenericEncoder)

    @classmethod
    def from_json(cls, json):
        return cls(**json)

class ExchangeRatesEncoder(json.JSONEncoder):
    def default(self, obj):
        d = obj.__dict__.copy()
        d['from'] = d['frm']
        del d['frm']
        return d

class ExchangeRates(JsonAware):
    def __init__(self, frm, to, rate):
        self.frm = frm
        self.to = to
        self.rate = rate

    def json(self):
        return json.dumps(self, cls=ExchangeRatesEncoder)

    @classmethod
    def from_json(cls, json):
        d = json.copy()
        d['frm'] = d['from']
        del d['from']
        return cls(**d)

# Test

a = ExchangeRates('a', 'b', 1.23)
print(a.json())

jdict = {"from": "z", "to": "y", "rate": 4.56, }

b = ExchangeRates.from_json(jdict)
print(b.json())    

typical output

{"from": "a", "to": "b", "rate": 1.23}
{"from": "z", "to": "y", "rate": 4.56}

Solution 2

Add a single underscore to your preferred names: from_ and to_

(see PEP 8)

class ExchangeRates(JsonAware):
    def __init__(self, from_, to_, rate):
        self.from = from_
        self.to = to_
        self.rate = rate

Solution 3

Use a synonym. Try "origin" or "source" instead.

Share:
10,764

Related videos on Youtube

user4659009
Author by

user4659009

Updated on October 21, 2022

Comments

  • user4659009
    user4659009 over 1 year

    I have the following class with the variables from, to and rate. from is a keyword. If I want to use it in the init method below, what's the correct way to write it?

    More context: The class needs the from variable explicitly as it's part of a json required by a POST endpoint written up by another developer in a different language. So changing the variable name is out of the question.

    class ExchangeRates(JsonAware):
        def __init__(self, from, to, rate):
            self.from = from
            self.to = to
            self.rate = rate
    

    JsonAware code:

    class PropertyEquality(object):
        def __eq__(self, other):
            return (isinstance(other, self.__class__) and self.__dict__ == other.__dict__)
    
        def __ne__(self, other):
            return not self.__eq__(other)
    
        def __repr__(self):
            return '%s(%s)' % (self.__class__.__name__, ', '.join(['%s=%s' % (k, v) for (k, v) in self.__dict__.items()]))
    
    class JsonAware(PropertyEquality):
        def json(self):
            return json.dumps(self, cls=GenericEncoder)
    
        @classmethod
        def from_json(cls, json):
            return cls(**json)
    

    GenericEncoder code:

    class GenericEncoder(json.JSONEncoder):
        def default(self, obj):
            return obj.__dict__
    
    • jonrsharpe
      jonrsharpe about 8 years
      You can't use it as an identifier, because it's a keyword. That's what keyword means! Use e.g. from_ instead.
    • jonrsharpe
      jonrsharpe about 8 years
      You could do it that way if you like: setattr(self, 'from', kwargs.get('from')), but then you have to pass it in via a dictionary too: rates = ExchangeRates(..., **{'from': whatever}) and can only access it via getattr(rates, 'from'). It's much less awkward to rename it. See e.g. stackoverflow.com/q/9746838/3001761
    • Dilettant
      Dilettant about 8 years
      flagged in red sounds like an IDE trying to assist the author ;-) I would follow @jonrsharpe's advice.
    • jonrsharpe
      jonrsharpe about 8 years
      You could expand the question with that context and a minimal reproducible example (what's JsonAware?), there are probably ways to handle parsing to and from JSON where the keys are keywords. But you definitely can't do it directly.
  • Eduardo Pignatelli
    Eduardo Pignatelli about 3 years
    Other languages, such as C#, work around this using the @ symbol, e.g. @string. It would be nice to have that in python, e.g. @lambda.
  • PM 2Ring
    PM 2Ring about 3 years
    @EduardoPignatelli In Python, the @ symbol is used to create a function or class decorator. The PEP 8 convention for names that clash with reserved words or builtin names is to append a single trailing underscore, as mentioned in Psionman's answer.
  • Eduardo Pignatelli
    Eduardo Pignatelli about 3 years
    The @ symbol is arbitrary, it can really be anything. The problem I find with the underscore is that it actually modifies the name of the variable (i.e. it will end with the underscore).