Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a Python 'shortcut' to define a class variable equal to a string version of its own name?

This is a bit of a silly thing, but I want to know if there is concise way in Python to define class variables that contain string representations of their own names. For example, one can define:

class foo(object):
    bar = 'bar'
    baz = 'baz'
    baf = 'baf'

Probably a more concise way to write it in terms of lines consumed is:

class foo(object):
    bar, baz, baf = 'bar', 'baz', 'baf'

Even there, though, I still have to type each identifier twice, once on each side of the assignment, and the opportunity for typos is rife.

What I want is something like what sympy provides in its var method:

sympy.var('a,b,c')

The above injects into the namespace the variables a, b, and c, defined as the corresponding sympy symbolic variables.

Is there something comparable that would do this for plain strings?

class foo(object):
    [nifty thing]('bar', 'baz', 'baf')

EDIT: To note, I want to be able to access these as separate identifiers in code that uses foo:

>>> f = foo(); print(f.bar)
bar

ADDENDUM: Given the interest in the question, I thought I'd provide more context on why I want to do this. I have two use-cases at present: (1) typecodes for a set of custom exceptions (each Exception subclass has a distinct typecode set); and (2) lightweight enum. My desired feature set is:

  1. Only having to type the typecode / enum name (or value) once in the source definition. class foo(object): bar = 'bar' works fine but means I have to type it out twice in-source, which gets annoying for longer names and exposes a typo risk.
  2. Valid typecodes / enum values exposed for IDE autocomplete.
  3. Values stored internally as comprehensible strings:

    1. For the Exception subclasses, I want to be able to define myError.__str__ as just something like return self.typecode + ": " + self.message + " (" + self.source + ")", without having to do a whole lot of dict-fu to back-reference an int value of self.typecode to a comprehensible and meaningful string.
    2. For the enums, I want to just be able to obtain widget as output from e = myEnum.widget; print(e), again without a lot of dict-fu.

      • I recognize this will increase overhead. My application is not speed-sensitive (GUI-based tool for driving a separate program), so I don't think this will matter at all.
  4. Straightforward membership testing, by also including (say) a frozenset containing all of the typecodes / enum string values as myError.typecodes/myEnum.E classes. This addresses potential problems from accidental (or intentional.. but why?!) use of an invalid typecode / enum string via simple sanity checks like if not enumVal in myEnum.E: raise(ValueError('Invalid enum value: ' + str(enumVal))).

  5. Ability to import individual enum / exception subclasses via, say, from errmodule import squirrelerror, to avoid cluttering the namespace of the usage environment with non-relevant exception subclasses. I believe this prohibits any solutions requiring post-twiddling on the module level like what Sinux proposed.
  6. For the enum use case, I would rather avoid introducing an additional package dependency since I don't (think I) care about any extra functionality available in the official enum class. In any event, it still wouldn't resolve #1.

I've already figured out implementation I'm satisfied with for all of the above but #1. My interest in a solution to #1 (without breaking the others) is partly a desire to typo-proof entry of the typecode / enum values into source, and partly plain ol' laziness. (Says the guy who just typed up a gigantic SO question on the topic.)

like image 1000
hBy2Py Avatar asked May 07 '15 03:05

hBy2Py


People also ask

How do you define a class variable in Python?

A class variable is declared inside of class, but outside of any instance method or __init__() method. By convention, typically it is placed right below the class header and before the constructor method and other methods.

How do you set a string to equal a variable in Python?

To assign it to a variable, we can use the variable name and “=” operator. Normally single and double quotes are used to assign a string with a single line of character but triple quotes are used to assign a string with multi-lines of character. This is how to declare and assign a variable to a string in Python.

What is __ str __ in Python?

Python __str__()This method returns the string representation of the object. This method is called when print() or str() function is invoked on an object. This method must return the String object.

How do you use a string name as a variable in Python?

String Into Variable Name in Python Using the vars() Function. Instead of using the locals() and the globals() function to convert a string to a variable name in python, we can also use the vars() function. The vars() function, when executed in the global scope, behaves just like the globals() function.


2 Answers

I recommend using collections.namedtuple:

Example:

>>> from collections import namedtuple as nifty_thing
>>> Data = nifty_thing("Data", ["foo", "bar", "baz"])
>>> data = Data(foo=1, bar=2, baz=3)
>>> data.foo
1
>>> data.bar
2
>>> data.baz
3

Side Note: If you are using/on Python 3.x I'd recommend Enum as per @user2357112's comment. This is the standardized approach going forward for Python 3+

Update: Okay so if I understand the OP's exact requirement(s) here I think the only way to do this (and presumably sympy does this too) is to inject the names/variables into the globals() or locals() namespaces. Example:

#!/usr/bin/env python


def nifty_thing(*names):
    d = globals()
    for name in names:
        d[name] = None


nifty_thing("foo", "bar", "baz")

print foo, bar, bar

Output:

$ python foo.py 
None None None

NB: I don't really recommend this! :)

Update #2: The other example you showed in your question is implemented like this:

#!/usr/bin/env python


import sys


def nifty_thing(*names):
    frame = sys._getframe(1)
    locals = frame.f_locals

    for name in names:
        locals[name] = None


class foo(object):

    nifty_thing("foo", "bar", "baz")


f = foo()

print f.foo, f.bar, f.bar

Output:

$ python foo.py 
None None None

NB: This is inspired by zope.interface.implements().

like image 141
James Mills Avatar answered Oct 23 '22 17:10

James Mills


current_list  = ['bar', 'baz', 'baf']

class foo(object):
    """to be added"""

for i in current_list:
    setattr(foo, i, i)

then run this:

>>>f = foo()
>>>print(f.bar)
bar
>>>print(f.baz)
baz
like image 26
Sinux Avatar answered Oct 23 '22 17:10

Sinux