0

I have a python class,called settings, which includes an __init__ method for setting some values as below:

class settings:
    global appkey
    global appversion
    def __init__(self):
        appkey = 1
        appversion = 1
        applicationname = "app1"
        applicationfile = "app.txt"

In another python file(main script), I define an instance of my settings class via this codes:

import settings
from settings import settings
set = settings()
print set.appversion
print set.appkey
print set.applicationfile

But when I run my main python script, I got this error:

AttributeError: settings instance has no attribute 'appversion'

I expect that when I am defining an instance of a class, settings class here, its init function will be triggered and I will have values for its attributes.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
Stateless
  • 293
  • 2
  • 4
  • 18
  • 3
    You should write `self.appkey = 1`, etc. – Willem Van Onsem May 08 '17 at 12:27
  • with self every thing is ok! – Stateless May 08 '17 at 12:30
  • 1
    Another piece of advice would be to use any the prefix `_` on any class variables. So in `init` you might want to intialize your varibales like this: `self._appkey = 1`. It really doesn't make a huge amount of difference for you, but it would possibly help people understand your code if you have issues later since the underscore `_` is a sign of a class variable. :) – geostocker May 08 '17 at 12:32
  • self._ is a good idea. thanks @geostocker – Stateless May 08 '17 at 12:45

3 Answers3

6

The variables are local to the __init__ of your class. To have them as instance attributes, you need to bind them to the instance using say a dot reference to the instance:

def __init__(self):
    self.appkey = 1
    ...

Synonymous (but less verbose) to binding attributes to/setting attributes on the instance using setattr:

def __init__(self):
    setattr(self, 'appkey', 1)
    ...

On another note, you don't need that global statement since you only want to set new attributes on the instance; nothing to do with the global namespace.

You could easily check the behavior of the new __init__ as being different from the previous by analyzing the bytecode:

from dis import dis

class Settings(object):
   def __init__(self):
       self.appkey = 1  

dis(Settings.__init__)

3           0 LOAD_CONST               1 (1)
            2 LOAD_FAST                0 (self)
            4 STORE_ATTR               0 (appkey)
            6 LOAD_CONST               0 (None)
            8 RETURN_VALUE

Notice how we are not calling the popular STORE_FAST as with a vanilla assignment but STORE_ATTR.

Moses Koledoye
  • 77,341
  • 8
  • 133
  • 139
  • A little addition to this would be to use the underscore prefix on the class variables: `self._appkey` to improve readability and properly clarify that it's a class/private variable. – geostocker May 08 '17 at 12:48
3

So far you have only created "normal" variables in your __init__() method. They are only accessible locally within that function. If you want to create instance attributes you have to assign them to self:

class settings:

    def __init__(self):
        self.appkey = 1
        self.appversion = 1
        self.applicationname = "app1"
        self.applicationfile = "app.txt"
Mike Scotty
  • 10,530
  • 5
  • 38
  • 50
0

I believe I had a similar problem some time ago, not sure my solution is the best practice, but it did achieve my goal that was to create a datetime variable for each user, getting the start time of a survey, I simply needed to declare the variable global in the Get method (survey start), so I could be able to pass it to the Post method (survey ended). Then I could compare start time to the end time, and calculate time spent answering the survey for each participant. After a review, I see other more simpler way to reach the same goal, in any case, here is the code I used:

class EmView(DistanceMixin, RegistredIP, View):

def __init__(self):
    self.dhms = datetime.now()

def get(self, request):

# DHMS ==> Date Time Hour Minutes Seconds survey started

    template_name = "survey/survey_section_a.html"

    global DHMS_START
    DHMS_START = self.dhms

        context_data = {
            "survey_form": survey_form,
            }

        return render(request, template_name, context_data)


 def post(self, request):

    dhms_start = DHMS_START
    dhms_end = datetime.now()
    dhms_survey_duration = dhms_end - dhms_start

 # other codes not included...