python using __init__ vs just defining variables in class - any difference? - python

I'm new to Python - and just trying to better understand the logic behind certain things.
Why would I write this way (default variables are in __init__):
class Dawg:
def __init__(self):
self.previousWord = ""
self.root = DawgNode()
self.uncheckedNodes = []
self.minimizedNodes = {}
def insert( self, word ):
#...
def finish( self ):
#...
Instead of this:
class Dawg:
previousWord = ""
root = DawgNode()
uncheckedNodes = []
minimizedNodes = {}
def insert( self, word ):
#...
def finish( self ):
#...
I mean - why do I need to use __init__ -> if I can just as easily add default variables to a class directly?

When you create variables in the Class, then they are Class variables (They are common to all the objects of the class), when you initialize the variables in __init__ with self.variable_name = value then they are created per instance and called instance variables.
For example,
class TestClass(object):
variable = 1
var_1, var_2 = TestClass(), TestClass()
print var_1.variable is var_2.variable
# True
print TestClass.variable is var_1.variable
# True
Since variable is a class variable, the is operator evaluates to True. But, in case of instance variables,
class TestClass(object):
def __init__(self, value):
self.variable = value
var_1, var_2 = TestClass(1), TestClass(2)
print var_1.variable is var_2.variable
# False
print TestClass.variable is var_1.variable
# AttributeError: type object 'TestClass' has no attribute 'variable'
And you cannot access an instance variable, with just the class name.

When you write this:
class Dawg:
previousWord = ""
root = DawgNode()
uncheckedNodes = []
minimizedNodes = {}
Those are not instance variables, they're class variables (meaning: the same variables with the same values are shared between all instances of the class.) On the other hand, this:
class Dawg:
def __init__(self):
self.previousWord = ""
self.root = DawgNode()
self.uncheckedNodes = []
self.minimizedNodes = {}
... Is declaring instance variables, meaning: the values are different for each instance of the class. As you see, each snippet means a completely different thing, and you have to pick the one that is appropriate for you. Hint: most of the time you're interested in instance variables, because class variables define a kind of shared global state for your objects, which is error prone.

Related

Prevent user of changing variable type in python

I have main class with a lot of attributes that are initially defined as an object of a Prop class. This Prop class has two attributes: its value and the options, which is a list of acceptable values for the attribute.
class Prop():
def __init__(self, value, *options):
self.value = value
self.options = options
class Main():
def __init__(self):
self._prop1 = Prop(None)
self._prop2 = Prop(None)
The first important thing here is that _propx has to be an instance variable, since I will create more than one instance of Main.
The values of a Prop instance can either be a string or an integer, but the problem with this code is that I have to be sure that the user will do something like main._prop1.value = 1 and not main._prop1 = 1 otherwise it would break my code when doing _prop1.options. I don't want to use traits, thus I decided to make each _propx instance a kind of property, but I'm talking about a lot of instances and I don't want to define each setter especially because they will be all the same.
I found two solutions to solve this problem, the first is by using the same setter to all properties:
class Main():
def __init__(self):
self._prop1 = Prop(None)
self._prop2 = Prop(None)
def set_prop(attr):
def set_value(self, value):
self.__dict__[attr].value = value
return set_value
prop1 = property(fset=set_prop('_prop1'))
prop2 = property(fset=set_prop('_prop2'))
The second is by using an auxiliary class and redefine its __set__:
class Aux():
def __set_name__(self, owner, name):
self.public_name = name
self.private_name = '_' + name
def __set__(self, obj, value):
print(self, obj, value, self.private_name)
obj.__dict__[self.private_name].value = value
class Main():
def __init__(self):
self._prop1 = Prop(None)
self._prop2 = Prop(None)
prop1 = Aux()
prop2 = Aux()
the first on seems cleaner, but I have to pass the private name of each variable and I have to write the setter in the Main which I don't like because I would it to be as clean as possible. By other hand, in the second I have to use an auxiliary class.
My question is: is there a way of defining the setter in the Prop class? The reason why I couldn't find a way of doing this is that the Aux.__set__ seems to work only when I create an Aux instance as a class variable (static variable). This is also why I have to create a private and a public variable for each property. Is there a way of using __set__ to an instance (non-static) variable?

Python Variables across Class functions - how to call them?

Instead of using a global variable, I'm trying to make an instance of a variable in a class, as it seems to be best practice. How do I reference this variable across other functions in the class? I would have thought that Test.running_sum would work or at least running_sum in test_function, but I'm not having any luck with either. Thanks very much!
class Test:
def __init__(self):
self.root = None
running_sum = 0
def test_function(self):
print(Test.running_sum)
return
x = Test()
x.test_function()
Error:
Traceback (most recent call last):
File "so.py", line 1, in <module>
class Test:
File "so.py", line 10, in Test
x = Test()
NameError: name 'Test' is not defined
Use self parameter provided in the method signature.
Note that what you wrote is not a method, but an external function using class Test. To write a method of Test, the def should be at one level of indentation inside class Test as following:
class Test:
def __init__(self):
self.running_sum = 0
def test_function(self):
print(self.running_sum)
There are several things to add if you want an explanation behind this "best practice".
Assuming you write the following code:
class Test:
numbers = []
def add(self, value):
self.numbers.append(value)
The Test.numbers list is instantiated once and shared accross all instances of Test. Therefore, if 2 different instances add to the list, both act on the same list:
a = Test()
b = Test()
a.add(5)
b.add(10)
assert a.numbers == b.numbers == Test.numbers
When creating instance variables in the __init__ function, __init__ will be run at each instantiation, and therefore, the list will no longer be shared because they will be created for each individual instances.
class Test:
def __init__(self):
self.numbers = []
def add(self, number):
self.numbers.append(number)
a = Test()
b = Test()
a.add(5)
b.add(10)
assert a != b
As an object attribute: each object gets its own.
Test is the class; self is the Test object that invoked the method.
class Test:
def __init__(self):
self.root = None
self.running_sum = 0
def test_function(self):
self.running_sum += 1
print(self.running_sum)
return
x = Test()
y = Test()
x.test_function()
y.test_function()
Output:
1
1
As a class attribute: all objects share the same variable.
self.__class__ is the class of the invoking object (i.e. Test).
class Test:
running_sum = 0
def __init__(self):
self.root = None
def test_function(self):
self.__class__.running_sum += 1
print(self.__class__.running_sum)
return
x = Test()
y = Test()
x.test_function()
y.test_function()
Output:
1
2
how do I reference this variable across other functions in the class
Several things I see wrong here. First of all, you are calling running_sum on the class itself which doesn't make sense since you are declaring running_sum as an attribute of an instance of Test. Second, from the way you formatted your question, it seems that test_function is outside of the class Test which doesn't make sense since you are passing self to it, implying it is an instance method. To resolve you could do this:
class Test:
def __init__(self):
self.running_sum = 0
def test_function(self):
print(self.running_sum)
Then again this also is weird... Why would you need a "test_function" when you can simply test the value of running_sum by simply doing:
x = Test()
x.running_sum
In your __init__ function, you've created a local variable. That variable will no longer exist after the function has completed.
If you want to create a variable specific to the object x then you should create a self.running_sum variable
class Test:
def __init__(self):
self.root = None
self.running_sum = 0
def test_function(self):
print(self.running_sum)
If you want to create a variable specific to the class Test then you should create a Test.running_sum variable.
class Test:
running_sum = 0
def __init__(self):
self.root = None
def test_function(self):
print(Test.running_sum)

confused value scope in python

Hi I have staring programming with c and I conld'nt understand value scope in python.
here is my code
class ScenarioEnvironment():
def __init__(self):
print(self)
class report():
config = ScenarioEnvironment()
def __init__(self):
self.config = ScenarioEnvironment()
what happens passing config and config at init()?
and I wonder the value scope which config will be class valuable?
You need to know the differences between class attribute and instance object attribute.
Maybe these codes will help you:
class TestConfig1(object):
config = 1
def __init__(self):
self.config = 2
class TestConfig2(object):
config = 1
def __init__(self):
self.config2 = 2
if __name__ == "__main__":
print TestConfig1.config
t = TestConfig1()
print t.config
t2 = TestConfig2()
print t2.config
print t2.config2
more you can see the python blog.click here
Since your question seems a bit ambiguous, I'll just comment/fix your code:
class ScenarioEnvironment():
def __init__(self,x):
self.x = x # Assigning instance variable x to constructor parameter x.
print(self) # You're printing the object instance.
class report():
# Static variable shared amongst all classes.
config = ScenarioEnvironment(None) # Assigned to new instance of ScenarioEnvironment.
def __init__(self):
# No argument, must pass one (None).
# self.config is to a new ScenarioEnvironment instance.
self.config = ScenarioEnvironment(None)
Lets try out the classes.
Output:
s = ScenarioEnvironment(None)
r = report()
>>> <__main__.ScenarioEnvironment instance at 0x026F4238>
>>> <__main__.ScenarioEnvironment instance at 0x026F4300>
>>> <__main__.ScenarioEnvironment instance at 0x026F4350>

Can I iterate over a class in Python?

I have a class that keeps track of its instances in a class variable, something like this:
class Foo:
by_id = {}
def __init__(self, id):
self.id = id
self.by_id[id] = self
What I'd like to be able to do is iterate over the existing instances of the class. I can do this with:
for foo in Foo.by_id.values():
foo.do_something()
but it would look neater like this:
for foo in Foo:
foo.do_something()
is this possible? I tried defining a classmethod __iter__, but that didn't work.
If you want to iterate over the class, you have to define a metaclass which supports iteration.
x.py:
class it(type):
def __iter__(self):
# Wanna iterate over a class? Then ask that class for iterator.
return self.classiter()
class Foo:
__metaclass__ = it # We need that meta class...
by_id = {} # Store the stuff here...
def __init__(self, id): # new isntance of class
self.id = id # do we need that?
self.by_id[id] = self # register istance
#classmethod
def classiter(cls): # iterate over class by giving all instances which have been instantiated
return iter(cls.by_id.values())
if __name__ == '__main__':
a = Foo(123)
print list(Foo)
del a
print list(Foo)
As you can see in the end, deleting an instance will not have any effect on the object itself, because it stays in the by_id dict. You can cope with that using weakrefs when you
import weakref
and then do
by_id = weakref.WeakValueDictionary()
. This way the values will only kept as long as there is a "strong" reference keeping it, such as a in this case. After del a, there are only weak references pointing to the object, so they can be gc'ed.
Due to the warning concerning WeakValueDictionary()s, I suggest to use the following:
[...]
self.by_id[id] = weakref.ref(self)
[...]
#classmethod
def classiter(cls):
# return all class instances which are still alive according to their weakref pointing to them
return (i for i in (i() for i in cls.by_id.values()) if i is not None)
Looks a bit complicated, but makes sure that you get the objects and not a weakref object.
Magic methods are always looked up on the class, so adding __iter__ to the class won't make it iterable. However the class is an instance of its metaclass, so the metaclass is the correct place to define the __iter__ method.
class FooMeta(type):
def __iter__(self):
return self.by_id.iteritems()
class Foo:
__metaclass__ = FooMeta
...
Try this:
You can create a list with a global scope, define a list in the main module as follows:
fooList = []
Then add:
class Foo:
def __init__(self):
fooList.append(self)
to the init of the foo class
Then everytime you create an instance of the Foo class it will be added to the fooList list.
Now all you have to do is iterate through the array of objects like this
for f in fooList:
f.doSomething()
You can create a comprehension list and then call member methods as follows:
class PeopleManager:
def __init__(self):
self.People = []
def Add(self, person):
self.People.append(person)
class Person:
def __init__(self,name,age):
self.Name = name
self.Age = age
m = PeopleManager()
[[t.Name,t.Age] for t in m.People]
call to fill the object list:
m = PeopleManager()
m.Add( Person("Andy",38))
m.Add( Person("Brian",76))
You can create a class list and then call append in the init method as follows:
class Planet:
planets_list = []
def __init__(self, name):
self.name = name
self.planets_list.append(self)
Usage:
p1 = Planet("earth")
p2 = Planet("uranus")
for i in Planet.planets_list:
print(i.name)

Creating an instance of type(self) dynamically without calling __init__?

This is quite hard to explain. I have a class which should support the method copy_stateonly(). It should return a crippled version of the object which only contains the (copied) data members that I want. I hope this example explains it better:
# everything inherits from this
class SuperBase:
def __init__(self):
self.state_var = 3 # this should be copied into future objects
self.non_state_var = 0 # we don't want to copy this
def copy_stateonly(self):
newobj = # ??????????? create instance without calling __init__
newobj.state_var = self.state_var
return newobj
# some clases inherit from this
class Base(SuperBase):
def __init__(self):
SuperBase.__init__(self)
self.isflying = True # we want to copy this, this is state
self.sprite = "sprites/plane_generic.png" # we must drop this
def copy_stateonly(self):
newobj = SuperBase.copy_stateonly(self)
newobj.isflying = self.isflying
return newobj
class A144fighter(Base):
def __init__(self, teamname): # note required __init__ argument
Base.__init__(self)
self.colors = ["black", "grey"] # we want to copy this, this is state
self.name = teamname # we must drop this
def copy_stateonly(self):
newobj = Base.copy_stateonly(self)
newobj.colors = self.colors[:]
return newobj
plane = A144fighter("team_blue")
plane_state = plane.copy_stateonly() # this should return an A144fighter object with only state_var, flying and colors set.
Python 2.7
I'm not aware of a way to create new instances of classic classes (which is what you used in your example) without calling __init__(). New instances of new-style classes (descendants of object) can be created using
object.__new__(cls)
where cls is the type of object you would like to create.
An alternative is to use copy.copy() for copying, possibly overwriting __getstate__() and __setstate__() to define what should be copied.
Edit: To create a new instance of a classic class cls without calling __init__(), you can use the following hack:
class EmptyClass:
pass
new_instance = EmptyClass()
new_instance.__class__ = cls
new_instance.__dict__.update(whatever)
Remember that every object has a attribute named __class__. If you do <object>.__class__ it, will return that object's class object (if that makes sense). The class object is callable so you can add parentheses to the end to create a new instance of that class.
newobj = self.__class__()
# everything inherits from this
class SuperBase:
def __init__(self):
self.state_var = 3 # this should be copied into future objects
self.non_state_var = 0 # we don't want to copy this
def __getstate__(self):
return { 'state_var' : self.state_var }
def __str__(self):
return self.__class__.__name__ + '(' + str(vars(self)) + ')'
# some clases inherit from this
class Base(SuperBase):
def __init__(self):
SuperBase.__init__(self)
self.isflying = True # we want to copy this, this is state
self.sprite = "sprites/plane_generic.png" # we must drop this
def __getstate__(self):
state = SuperBase.__getstate__(self)
state['isflying'] = self.isflying
return state
class A144fighter(Base):
def __init__(self, teamname): # note required __init__ argument
Base.__init__(self)
self.colors = ["black", "grey"] # we want to copy this, this is state
self.name = teamname # we must drop this
def __getstate__(self):
state = Base.__getstate__(self)
state['colors'] = self.colors[:]
return state
plane = A144fighter("team_blue")
print plane
import copy
print copy.copy(plane)
# or manually:
import types
print types.InstanceType(plane.__class__, plane.__getstate__())

Categories

Resources