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)

Community
  • 1
  • 1
sdaau
  • 36,975
  • 46
  • 198
  • 278

3 Answers3

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.

juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172
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
Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
  • Thanks @StephenRauch - I think I like this approach best, cheers! – sdaau Apr 07 '17 at 22:12
  • 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. – juanpa.arrivillaga Apr 07 '17 at 22:19
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...

Community
  • 1
  • 1
sdaau
  • 36,975
  • 46
  • 198
  • 278