1

In my case, I'd like to save and restore some "plain" variables (i.e., ints, strings) in a file, which would end up as class properties. This example is the closest I got, by using import:

a.py

b = 134
a = "hello"

mytest.py

import inspect

class Teost:
  from a import *
  def __init__(self):
    self.c = 12
    print(inspect.getmembers(self)) # has a and b
    print(self.__dict__)            # no a and b
    print(self.a)                   # prints "hello"

xx = Teost()

So, here a.py acts as the file storing the variable values (a and b), and from a import * inside the class gets them as class properties (self.a and self.b), which is pretty much what I want.

Unfortunately, turns out using starred import syntax in a class is frowned upon:

$ python mytest.py
mytest.py:3: SyntaxWarning: import * only allowed at module level
  class Teost:
[('__doc__', None), ('__init__', <bound method Teost.__init__ of <__main__.Teost instance at 0x7fdca368ab90>>), ('__module__', '__main__'), ('a', 'hello'), ('b', 134), ('c', 12)]
{'c': 12}
hello

... so I get an ugly "SyntaxWarning: import * only allowed at module level", which I cannot get rid of (unless I disable warnings, which I don't want to do)

So, do I have any other options, to use a file written as a.py (i.e. in plain-text, Python syntax), and have the variables in it end up as some classes properties?

(I've seen How do I save and restore multiple variables in python?, but I'm not interested in pickle or shelve because neither of them writes in a Python-syntax, plain-text file)

3 Answers 3

1

You can import the module into your class like:

Code:

class Teost:
    import a as _a_py_attrs

    def __init__(self):
        for name in dir(Teost._a_py_attrs):
            if not name.startswith('__'):
                setattr(self, name, getattr(Teost._a_py_attrs, name))

Test Code:

xx = Teost()
print(xx.__dict__)
print(xx.a)

Results:

{'a': 'hello', 'b': 134}
hello

As Class Attributes:

If it would be preferable to have these as class attributes, instead of instance attributes, you can do:

class Teost:
    """ My Test Class """

import a as _a_py_attrs
for name in dir(_a_py_attrs):
    if not name.startswith('__'):
        setattr(Teost, name, getattr(_a_py_attrs, name))

Test Code:

xx = Teost()
print(xx.__dict__)
print(xx.a)

Results:

{}
hello
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks @StephenRauch - I think I like this approach best, cheers!
Wait, but setattr(self, name, ....) will set name as an instance attribute, not a class attribute... You could do this in a metaclass though. Or use setattr(Teost, name, ...) to set to a class attribute.
1

I mean, you could do something super hacky:

import inspect
import a

class A:
  def __init__(self):
    self.c = 12
    print(('a', 'hello')  in inspect.getmembers(self)) # has a and b
    print(('b', 134) in inspect.getmembers(self))
    print('a' in self.__dict__)            # no a and b
    print('b' in self.__dict__)
    print(self.a)                   # prints "hello"

for name in dir(a):
    if not name.startswith('__'): # very brittle here
        val = vars(a)[name]
        setattr(A, name, val)

x = A()

You would probably want to wrap the above logic in a metaclass.

Maybe just using exec is cleaner. It shouldn't be too big of a problem if you trust the source of a.py.

1 Comment

Thanks @juanpa.arrivillaga - good to have this approach in mind, too; cheers!
0

Well, found a workaround (which doesn't raise errors or warnings) - instead of import, read the file and then exec (not eval, eval SyntaxError: invalid syntax in python) it:

  #from a import *
  with open('a.py') as x: fstr = x.read()
  exec(fstr)

... although I should probably feel uncomfortable for using exec...

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.