Access global variable from external class - python

How to structure an application so global variables work ?
I'm looking for the simplest way to do something like :
main.py
from Class_A import Class_A
my_var = 3
c_a = Class_A()
print c_a.get_var()
# ==> NameError: global name 'my_var' is not defined
Class_A.py
class Class_A:
def get_var():
global my_var
return my_var
Answer :
Nagasaki45's answer is good, here is how to implement it :
main.py
from Class_A import Class_A
import Global_Vars
c_a = Class_A()
print Global_Vars.my_var
print c_a.get_var()
Global_Vars.my_var += 12
print c_a.get_var()
Class_A.py
import Global_Vars
class Class_A:
def get_var(self):
return Global_Vars.my_var
Global_Vars.py
import random
my_var = random.randint(0,100)
This outputs :
17
17
29

If two modules depends on same variable I would move it outside to a 3rd module and import it from there to both of them. Doing so you will end up with much less problems of circular imports.
In any case, the global keyword is used for setting global variables. Don't use it if you only want to get a variable value.

If you want some object to be shared by other objects, pass this objects to the others, ie:
shared.py :
import random
class Shared(object):
def __init__(self):
self.var = random.randint(0,100)
lib.py :
class MyClass(object):
def __init__(self, shared):
self.shared = shared
def get_var(self):
return self.shared.var
main.py :
from shared import Shared
import lib
shared = Shared()
obj = lib.MyClass(shared)
print "shared.var : ", shared.var
print "obj.get_var() : ", obj.get_var()
shared.var = 200
print "shared.var : ", shared.var
print "obj.get_var() : ", obj.get_var()
Note that you don't need any "global" variable here.

Hum ... am I totally wrong, of the real answer does not have anything to do with globals ?
You have to know that Python variables are mostly tags that references values. And integer are immutable. So, after that call:
gv.my_var += 12
gv.my_var references a different value (36). This only change this reference. Other references to 24 are not affected.

Related

How to write python global variables

I am trying to access a global variable in python inside a function, but it's value is not changing inside the function as apparently it doesn't refer to the global variable but it considers it a new variable.
here as below, I want to change the value of is_new_namespace = Trueinside the functiontest`
This is what I did and it doesn't recognize the variable but considers it as a newly created variable inside the function itself.
And one thing I need again is, can I access this variable is_new_namespaceinside another python file in the same directory? if yes, how?
from BitesizeDecorator import BitesizeDecorator
import execute
import constants
import subprocess
from custom_check_kubectl import does_kubectl_work
class CreateNamesapce(BitesizeDecorator):
def __init__(self, createnamesapce):
super(CreateNamesapce, self).__init__(createnamesapce)
is_new_namespace = False #global variable
def test(self):
super(CreateNamesapce, self).test()
if does_kubectl_work(self) != 0: # works
does_namespace_available = execute.check_if_exists("kubectl get ns | grep -E \'(^|\s)"+constants.NAMESPACE+"($|\s)\'")
if does_namespace_available != "" and len(does_namespace_available) != 0 : #if exists
print(constants.ORANGE+"\n[6] "+ u'\u0021' +constants.NC+" - "+constants.ORANGE+"Namespace \"" + constants.NAMESPACE +"\" already exists...\n" + constants.NC)
print(does_namespace_available)
else:
is_new_namespace = True #function considers this as a newly created variable
namespace_output = execute.subprocess_execute_arr(["kubectl", "create", "namespace", constants.NAMESPACE])
if namespace_output == 0: # returns zero if executed successfully
print(constants.GREEN+"\n[6] " + u'\u2714' +constants.NC+" - "+constants.GREEN+" Namespace " + constants.NAMESPACE + " created successfully..." + constants.NC + "\n")
else:
print(constants.RED+"\n[6] " + u'\u274C' +constants.NC+" - "+constants.RED+"error creating namespace \"" + constants.NAMESPACE + "\""+constants.NC+"\n")
else:
print(constants.RED + constants.ICON_CROSS + " \"Kubectl\" commmands are not working\n" + constants.NC)
Please see my comments on your code below
class CreateNamesapce(BitesizeDecorator):
def __init__(self, createnamesapce):
super(CreateNamesapce, self).__init__(createnamesapce)
# 1. this is a variable in this class, so it only exists in this class
is_new_namespace = False
def test(self):
super(CreateNamesapce, self).test()
# 2. this is treated as a new variable in this function only
is_new_namespace = True
# 3. this would properly reference and update the variable for the entire class,
# because of self (see comment 1.)
self.is_new_namespace = True
Adding is_new_namespace = False ABOVE the class definition means you can use it anywhere in this same file as intended, without needing self.
To reference this variable in other files, you would need to import it from wherever it was first created. For example, if your current code is in a file called file_a.py and you are in a new file called file_b.py you would need
from file_a import is_new_namespace
Now you have the variable that you created in file_a
Also, it is good practice to declare class variables first thing in the class, above def __init__(self)
class CreateNamesapce(BitesizeDecorator):
# declare class variables here
is_new_namespace = False
def __init__(self, createnamesapce):
super(CreateNamesapce, self).__init__(createnamesapce)

properties on a module? [duplicate]

This question already has answers here:
Can modules have properties the same way that objects can?
(8 answers)
Closed 6 years ago.
This probably already looks like a duplicate; here's the scenario:
default_config.py:
unit_id = -1 # a serial number; int. -1 is a test unit, for example
def um():
return unit_id % 60 # stagger uploads
upload_hour = 2 #am
upload_minute = property( um ) # <- something that works needed here...
config.py
from default_config import *
# Override defaults here, if necessary
unit_id = 12 # ACTUAL serial number...
some_file.py
import config as cfg
do_something(cfg.upload_hour, cfg.upload_minute)
print cfg.upload_minute * 5 # should be an int...?
So, the goals are:
A specific config file can override the defaults, which works fine
Some values which are calculated can be accessed - after the overrides are applied - but in a "transparent" way (ie. without the prop() brackets)
This seemed to be simple for python properties, but after various combinations, doesn't work. I guess its something to do with the function being defined on a module, not an object, and unbound first variables, etc...
Either I get a "property object" back, and can't then use operators on it, etc, or I can't get the value the property should calculate and return, or after many iterations I can't remember, some other error...
I guess its something to do with the function being defined on a
module, not an object...
Why not use an object then? :)
default_config.py
_DEFAULT_UNIT_ID = -1
_DEFAULT_UPLOAD_HOUR = 2
_MINUTES_PER_HOUR = 60
class BaseConfig(object):
def __init__(self, unit_id=_DEFAULT_UNIT_ID, upload_hour=_DEFAULT_UPLOAD_HOUR):
self.unit_id = unit_id
self.upload_hour = upload_hour
#property
def upload_minute(self):
return self.unit_id % _MINUTES_PER_HOUR
config.py
from default_config import BaseConfig
# organized place to put non-default parameters
config_dict = {
'unit_id': 12,
'upload_hour': 3,
}
CONFIG = BaseConfig(**config_dict)
some_file.py
from config import CONFIG
print CONFIG.upload_hour, CONFIG.upload_minute # "3 12"
I would also consider just combining default_config.py and config.py if there's no need to separate them, since it would be easier to see what keyword arguments the BaseConfig takes.
You can't define special methods on modules, but entries in sys.modules don't have to be module objects, they can also be class instance objects. This means you can take advantage of their attribute-access special methods like this:
default_config.py
class DefaultConfig(object):
unit_id = -1 # a serial number
upload_hour = 2 # am
#property
def upload_minute(self):
return self.unit_id % 60 # stagger uploads
config.py
import sys
from default_config import DefaultConfig
# override defaults
DefaultConfig.unit_id = 12
# see http://stackoverflow.com/questions/5365562/why-is-the-value-of-name-changing-after-assignment-to-sys-modules-name
# as to why the _ref is necessary
_ref, sys.modules[__name__] = sys.modules[__name__], DefaultConfig()
# clean up this module's namespace
del sys, DefaultConfig
some_file.py
from __future__ import print_function
import config as cfg
def do_something(hour, minute):
print('do_something({}, {}) called'.format(hour, minute))
do_something(cfg.upload_hour, cfg.upload_minute)
print(cfg.upload_minute * 5)
Output from running some_file.py:
do_something(2, 12) called
60

Serializable Static class variables in Python

Is it possible to have serializable static class variables or methods in python?
As an example suppose, I have the following code snippet:
import pickle
class Sample:
count = 0 # class variable
def __init__(self, a1=0, a2=0):
self.a = a1
self.b = a2
Sample.count += 1
#MAIN
f = open("t1.dat", "wb")
d = dict()
for i in range(10):
s = Sample(i, i*i)
d[i] = s
pickle.dump(d,f)
print "Sample.count = " + str(Sample.count)
f.close()
The output is:
Sample.count = 10
Now, I have another reader program similar to above:
import pickle
class Sample:
count = 0 # class variable
def __init__(self, a1=0, a2=0):
self.a = a1
self.b = a2
Sample.count += 1
#MAIN
f = open("t1.dat", "rb")
d = pickle.load(f)
print "Sample.count = " + str(Sample.count)
The output is:
Sample.count = 0
My question is:
How do I load the class variable from my file? In other words, how do I serialize a class variable? If directly not possible, is there any alternative? Please suggest.
Since class variable cannot be picked, as an alternative, I have used the code snippet in main part when reading from the file as below:
#MAIN
f = open("t1.dat", "rb")
d = pickle.load(f)
Sample.count = len(d.values())
print "Sample.count = " + str(Sample.count)
The output is now:
Sample.count = 10
Is it acceptable solution? Any other alternative?
Quoting the section on "What can be pickled and unpickled?"
Similarly, classes are pickled by named reference, so the same restrictions in the unpickling environment apply. Note that none of the class’s code or data is pickled, so in the following example the class attribute attr is not restored in the unpickling environment:
class Foo:
attr = 'a class attr'
picklestring = pickle.dumps(Foo)
So because attr, or in your case count, is part of the class definition, it never gets pickled. In your 'write' example, you're printing Sample.count which does exist but is not pickled in the first place.
You could store Sample.count in each instance as _count and put Sample.count = self._count. But remember that since your d is a dict, they may unpickle in any order. So essentially this won't work.
You'll need to add __setstate__ to your class customize the way it pickles and put in some flag value (like _count) which you then manipulate (via whatever logic works consistently) in __getstate__. (Edit: doesn't help with the given problem unless you store count in a global variable and access that in getstate and manipulate further each time an object is unpickled.)
Another potential workaround but yuck: Add a variable to your dict d so that it also gets pickled. When you read it back, restore with Sample.count = d['_count']. So before pickle.dump(d,f) when you pickle, do d['_count'] = Sample.count.
Important caveat: This is not actually allowing you to pickle Sample.count since what you're actually pickling (d) is a dictionary of Samples.
Edit: The Sample.count = len(d.values()) which you've put as a workaround is very specific to your use case and not to class attr's in general.

Can we find the _local_ class-name of an object instantiated from a class imported with 'import as'?

If I import a class and rename it by subclassing, it's fairly simple to find the new class name:
>>> from timeit import Timer
>>> class Test(Timer):
... pass
...
>>> test = Test()
>>> test.__class__.__name__
'Test'
However, if I alias the class as I import it, it retains the name from its host module:
>>> from timeit import Timer as Test2
>>> test2 = Test2()
>>> test2.__class__.__name__
'Timer'
Later, I want to provide user-facing output which is aware of the name they've given the class in their namespace. Consider:
def report_stats(timer):
print("Runtime statistics for %s:" % timer.__class__.__name__)
...
Is there a way to get a string reading "Test2", short of iterating over variables in the namespace to test for an exact match?
There's a really terrible answer to my own question; I won't be accepting this since it's probably pretty fragile (I only tested for a limited set of call circumstances). I mostly just hunted this down for the challenge; I will most likely be using something more durable for my actual use case.
This assumes we have access to the init function of the class we're trying to import as blah, and some sort of persistent external data store, at least for more complicated edge cases:
import inspect, dis
class Idiom(object):
description = None
alias = None
def __init__(self, desc):
global data_ob
self.description = desc
if self.__class__.__name__ == 'Idiom':
#cheat like hell to figure out who called us
self.alias = data_ob.name_idiom(inspect.currentframe().f_back)
else:
self.alias = self.__class__.__name__
class DataOb(object):
code = None
locations = {}
LOAD_NAME = 101
codelen = None
def name_idiom(self, frame):
if not self.code:
self.code = frame.f_code
self.codelen = len(self.code.co_code)
self.locations = {y:x for x, y in dis.findlinestarts(self.code)}
target_line = frame.f_lineno
addr_index = self.locations[target_line]+1
name_index = self.code.co_code[addr_index]
# there's a chance we'll get called again this line,
# so we want to seek to the next LOAD_NAME instance(101)
addr_index += 1
while addr_index < self.codelen:
if self.code.co_code[addr_index] == self.LOAD_NAME:
self.locations[target_line] = addr_index
break
addr_index += 1
return self.code.co_names[name_index]
The short explanation of how this works is:
we look up the previous frame from the init function
obtain the code object
find bytecode locations for the start of every line in the code
use the line-number from the frame to grab the bytecode location for the start of that line
locate a LOAD_NAME indicator in the bytecode for this line (I don't really follow this; my code assumes it'll be there)
look in the next bytecode position for an index which indicates which position in the code.co_names tuple contains the "name" of the LOAD_NAME call
From here we can do something like:
>>> from rabbit_hole import Idiom as timer_bob
>>> with timer_bob("down the rabbit hole"):
... waste_some_time = list(range(50000))
...
timer_bob: down the rabbit hole
runtime: 0:00:00.001909, children: 0:00:00, overhead: 0:00:00.001909

Global variables in Python aa

Hi I'm making a program on python and I'm having trouble adding a global variable to my program so I'm just going to post my code and show you how I tried to do it.
So this is my class:
import globalvariables
class Bus :
def __init__(self, Number, Capacity, Destination, Seats):
self.Bus_Number = Number
self.Bus_Capacity = Capacity
self.Bus_Destination = Destination
self.Seats_taken = Seats
def Book(self):
self.Seats_taken = Seats + 1
def ShowBus(self):
return (str(self.Bus_Number) + ", " + str(self.Bus_Capacity) + ", " + str(self.Bus_Destination) + ", " + str(self.Seats_taken))
and this is my module for global variables
Seats = 0
and this is what I'm trying to run:
import Transport
import globalvariables
Big_Red = Transport.Bus(1, 50, "NYC", 0)
Big_Red.Book()
print(Big_Red.ShowBus())
I'm getting this error:
Traceback (most recent call last):
File "D:\Python\Assignment 3\Tester.py", line 5, in <module>
Big_Red.Book()
File "D:\Python\Assignment 3\Transport.py", line 14, in Book
self.Seats_taken = Seats + 1
NameError: global name 'Seats' is not defined
The variable Seats is local to __init__ function and can't be accessed outside of it.
So,
self.Seats_taken = Seats + 1
Should be :
self.Seats_taken = self.Seats_taken + 1
or :
self.Seats_taken += 1
Instead of using global variables inside class you should use class attributes:
class Bus :
seats = 50 #shared across all instances
def __init__(self):
#code
def Book(self):
self.Seats_taken = self.seats + 1
Globals should be avoided. In case you still want it to be :
def Book(self):
self.Seats_taken = globalvariables.Seats + 1
When you import globalvariables, you gain access to names qualified by the module name: globalvariables.Seats. To import Seats into the namespace of another module, use from globalvariables import Seats. (In desperate cases, you can import all names from a module: from globalvariables import *, but usually you don't want this.)
When you define a function, it has its own local namespace. It includes all function's arguments.
Seats = 100
def foo(Seats):
# returns the value of variable named Seats
# defined within "def foo", *not* defined by "Seats = 100"
return Seats
print foo(200) # prints 200
print foo() # fails because Seats are not set
To initialize a function parameter, use default value:
def foo(seats=0):
print foo() # prints 0
print foo(55) # prints 55
Also, global variables are evil. Global constants are good.
You want to use a global variable to track seats taken. You'll be much better off if you encapsulate it into a class that only allows reasonable access, does not allow to set the value arbitrarily, log access if needed, etc:
class SeatsDispenser(object):
def __init__(self, initial_count):
self.count = initial_count
def allocate(self, number_of_seats):
self.count -= number_of_seats
if self.count < 0:
raise ValueError("Overcommitted!")
def seats_left(self):
return self.number_of_seats
Naming your variables, classes, constants, and functions with the same Title Case is impractical. Usually variables are lower_case, functions are lowerCamelCase, classes are TitleCamelCase and constants are ALL_CAPS.
A reasonable piece of code would look like this:
import constants # modules are usually lower case
import transport
def Bus(object):
def __init__(self, number, capacity, seats=constants.SEATS):
self.number = number
self.capacity = capacity
self.seats = seats
big_red = Bus(constants.NYC_BUS_NUMBER, 50, 25)
default_blue = Bus(1, 20) # seats not passed, the default value is used
seats_dispenser = SeatsDispenser(100)
seats_dispenser.allocate(big_red.count)
seats_dispenser.allocate(default_blue.count)
print seats_dispenser.seats.left()

Categories