Python classes self.variables - python

I have started learning python classes some time ago, and there is something that I do not understand when it comes to usage of self.variables inside of a class. I googled, but couldn't find the answer. I am not a programmer, just a python hobbyist.
Here is an example of a simple class, with two ways of defining it:
1)first way:
class Testclass:
def __init__(self, a,b,c):
self.a = a
self.b = b
self.c = c
def firstMethod(self):
self.d = self.a + 1
self.e = self.b + 2
def secondMethod(self):
self.f = self.c + 3
def addMethod(self):
return self.d + self.e + self.f
myclass = Testclass(10,20,30)
myclass.firstMethod()
myclass.secondMethod()
addition = myclass.addMethod()
2)second way:
class Testclass:
def __init__(self, a,b,c):
self.a = a
self.b = b
self.c = c
def firstMethod(self):
d = self.a + 1
e = self.b + 2
return d,e
def secondMethod(self):
f = self.c + 3
return f
def addMethod(self, d, e, f):
return d+e+f
myclass = Testclass(10,20,30)
d, e = myclass.firstMethod()
f= myclass.secondMethod()
addition = myclass.addMethod(d,e,f)
What confuses me is which of these two is valid?
Is it better to always define the variables inside the methods (the variables we expect to use later) as self.variables (which would make them global inside of class) and then just call them inside some other method of that class (that would be the 1st way in upper code)?
Or is it better not to define variables inside methods as self.variables, but simply as regular variables, then return at the end of the method. And then "reimport" them back into some other method as its arguments (that would be 2nd way in upper code)?
EDIT: just to make it clear, I do not want to define the self.d, self.e, self.f or d,e,f variables under the init method. I want to define them at some other methods like showed in the upper code.
Sorry for not mentioning that.

Both are valid approaches. Which one is right completely depends on the situation.
E.g.
Where you are 'really' getting the values of a, b, c from
Do you want/need to use them multiple times
Do you want/need to use them within other methods of the class
What does the class represent
Are a b and c really 'fixed' attributes of the class, or do they depend on external factors?
In the example you give in the comment below:
Let's say that a,b,c depend on some outer variables (for example a = d+10, b = e+20, c = f+30, where d,e,f are supplied when instantiating a class: myclass = Testclass("hello",d,e,f)). Yes, let's say I want to use a,b,c (or self.a,self.b,self.c) variables within other methods of the class too.
So in that case, the 'right' approach depends mainly on whether you expect a, b, c to change during the life of the class instance. For example, if you have a class where hte attributes (a,b,c) will never or rarely change, but you use the derived attribures (d,e,f) heavily, then it makes sense to calculate them once and store them. Here's an example:
class Tiger(object):
def __init__(self, num_stripes):
self.num_stripes = num_stripes
self.num_black_stripes = self.get_black_stripes()
self.num_orange_stripes = self.get_orange_stripes()
def get_black_stripes(self):
return self.num_stripes / 2
def get_orange_stripes(self):
return self.num_stripes / 2
big_tiger = Tiger(num_stripes=200)
little_tiger = Tiger(num_stripes=30)
# Now we can do logic without having to keep re-calculating values
if big_tiger.num_black_stripes > little_tiger.num_orange_stripes:
print "Big tiger has more black stripes than little tiger has orange"
This works well because each individual tiger has a fixed number of stripes. If we change the example to use a class for which instances will change often, then out approach changes too:
class BankAccount(object):
def __init__(self, customer_name, balance):
self.customer_name = customer_name
self.balance = balance
def get_interest(self):
return self.balance / 100
my_savings = BankAccount("Tom", 500)
print "I would get %d interest now" % my_savings.get_interest()
# Deposit some money
my_savings.balance += 100
print "I added more money, my interest changed to %d" % my_savings.get_interest()
So in this (somewhat contrived) example, a bank account balance changes frequently - therefore there is no value in storing interest in a self.interest variable - every time balance changes, the interest amount will change too. Therefore it makes sense to calculate it every time we need to use it.
There are a number of more complex approaches you can take to get some benefit from both of these. For example, you can make your program 'know' that interest is linked to balance and then it will temporarily remember the interest value until the balance changes (this is a form of caching - we use more memory but save some CPU/computation).
Unrelated to original question
A note about how you declare your classes. If you're using Python 2, it's good practice to make your own classes inherit from python's built in object class:
class Testclass(object):
def __init__(self, printHello):
Ref NewClassVsClassicClass - Python Wiki:
Python 3 uses there new-style classes by default, so you don't need to explicitly inherit from object if using py3.

EDITED:
If you want to preserve the values inside the object after perform addMethod, for exmaple, if you want call addMethod again. then use the first way. If you just want to use some internal values of the class to perform the addMethod, use the second way.

You really can't draw any conclusions on this sort of question in the absence of a concrete and meaningful example, because it's going to depend on the facts and circumstances of what you're trying to do.
That being said, in your first example, firstMethod() and secondMethod() are just superfluous. They serve no purpose at all other than to compute values that addMethod() uses. Worse, to make addMethod() function, the user has to first make two inexplicable and apparently unrelated calls to firstMethod() and secondMethod(), which is unquestionably bad design. If those two methods actually did something meaningful it might make sense (but probably doesn't) but in the absence of a real example it's just bad.
You could replace the first example by:
class Testclass:
def __init__(self, a,b,c):
self.a = a
self.b = b
self.c = c
def addMethod(self):
return self.a + self.b + self.c + 6
myclass = Testclass(10,20,30)
addition = myclass.addMethod()
The second example is similar, except firstMethod() and secondMethod() actually do something, since they return values. If there was some reason you'd want these values separately for some reason other than passing them to addMethod(), then again, it might make sense. If there wasn't, then again you could define addMethod() as I just did, and dispense with those two additional functions altogether, and there wouldn't be any difference between the two examples.
But this is all very unsatisfactory in the absence of a concrete example. Right now all we can really say is that it's a slightly silly class.
In general, objects in the OOP sense are conglomerates of data (instance variables) and behavior (methods). If a method doesn't access instance variables - or doesn't need to - then it generally should be a standalone function, and not be in a class at all. Once in a while it will make sense to have a class or static method that doesn't access instance variables, but in general you should err towards preferring standalone functions.

Related

self in Python classes

I know first argument in Python methods will be an instance of this class. So we need use "self" as first argument in methods. But should we also specify attribures (variables) in method starting with "self."?
My method work even if i don't specify self in his attributes:
class Test:
def y(self, x):
c = x + 3
print(c)
t = Test()
t.y(2)
5
and
class Test:
def y(self, x):
self.c = x + 3
print(self.c)
t = Test()
t.y(2)
5
For what i would need specify an attribute in methods like "self.a" instead of just "a"?
In which cases first example will not work but second will? Want to see situation which shows really differences between two of them, because now they behave the same from my point of view.
The reason you do self.attribute_name in a class method is to perform computation on that instances attribute as opposed to using a random variable.For Example
class Car:
def __init__(self,size):
self.size = size
def can_accomodate(self,number_of_people):
return self.size> number_of_people
def change_size(self,new_size):
self.size=new_size
#works but bad practice
def can_accomodate_v2(self,size,number_of_people):
return size> number_of_people
c = Car(5)
print(c.can_accomodate(2))
print(c.can_accomodate_v2(4,2))
In the above example you can see that the can_accomodate use's self.size while can_accomodate_v2 passes the size variable which is bad practice.Both will work but the v2 is a bad practice and should not be used.You can pass argument into a class method not related to the instance/class for example "number_of_people" in can_accomodate funtion.
Hope this helps.

DRY principle in Python __init__ method

In this class definition, every parameter occurs three times, which seems to violate the DRY (don't repeat yourself) principle:
class Foo:
def __init__(self, a=1, b=2.0, c=(3, 4, 5)):
self.a = int(a)
self.b = float(b)
self.c = list(c)
DRY could be applied like this (Python 3):
class Foo:
def __init__(self, **kwargs):
defaults = dict(a=1, b=2.0, c=[3, 4, 5])
for k, v in defaults.items():
setattr(self, k, type(v)(kwargs[k]) if k in kwargs else v)
# ...detect illegal keywords here...
However, this breaks IDE autocomplete (tried Spyder and Elpy) and pylint will complain if I try to access the attributes later on.
Is there a clean way to handle this?
Edit: The example has three parameters, but I find myself dealing with this when there are 15 parameters, where I only rarely need to override the defaults; often with more complicated types, where I would need to do
if not isinstance(kwargs['x'], SomeClass):
raise TypeError('x: must be SomeClass')
self.x = kwargs['x']
for each of them. Moreover, I can't use mutables as default values for keyword arguments.
Principles like DRY are important, but it's important to keep in mind the rationale for such a principle before blindly applying it -- arguably the biggest advantage of DRY code is that you increase the maintainability of the code by only having to modify it in one place and not having to risk the subtle bugs that can occur with code that is modified in one place and not another. DRY can be antithetical to other common principles like YAGNI and KISS, and choosing the correct balance for your application is important.
In particular, DRY often applies to default values, application logic, and other things that could cause bugs if changed in one place and not another. IMO variable names don't fit in the same way since refactoring the code to change every occurrence of Foo's instance variable of a won't actually break anything by not changing the name in the initializer as well.
With that in mind, we have a simple test for your code. Are these variables likely to change together, or is the initializer for Foo a layer of abstraction that allows a refactoring of the inputs independently of the class's instance variables?
Change Together: I rather like #chepner's answer, and I'd take it one step further. If your class is anything more than a data transfer object you can use #chepner's solution as a way to logically group related pieces of data (which admittedly could be unnecessary in your situation, and without some context it's difficult to choose an optimal way to introduce such an idea), e.g.
from dataclasses import dataclass, field
#dataclass
class MyData:
a: int
b: float
c: list
class Foo:
def __init__(self, my_data):
self.wrapped = my_data
Change Separately: Then just leave it alone, or KISS as they say.
As a preface, your code
class Foo:
def __init__(self, a=1, b=2.0, c=(3, 4, 5)):
self.a = int(a)
self.b = float(b)
self.c = list(c)
is, as mentioned in several comments, fine as it is. Code is read far more than it is written, and aside from needing to be careful to avoid typos in the names when first defining this, the intent is perfectly clear. (Though see the end of the answer regarding the default value of c.)
If you are using Python 3.7, you can use a data class to reduce the number of references you make to each variable.
from dataclasses import dataclass, field
from typing import List
#dataclass
class Foo:
a: int = 1
b: float = 2.0
c: List[int] = field(default_factory=lambda: [3,4,5])
This doesn't prevent you from violating the type hints (Foo("1") will happily set a = "1" instead of a = 1 or raising an error), but it's typically the responsibility of the caller to provide arguments of the correct type.) If you really want to enforce this at run-time, you can add a __post_init__ method:
def __post_init__(self):
self.a = int(self.a)
self.b = float(self.b)
self.c = list(self.c)
But if you do that, you may as well go back to your original hand-coded __init__ method.
As an aside, the standard idiom for mutable default arguments is
def __init__(self, a=1, b=2.0, c=None):
...
if c is None:
c = [3, 4, 5]
Your approach has two problem:
It requires that list be run for every instantiation, rather than letting the compiler hard-code [3,4,5].
If you were type-hinting the arguments to __init__, your default value doesn't match the intended type. You'd have to write something like
def init(a: int = 1, b: float = 2.0, c : Union[List[Int], Tuple[Int,Int,Int]] = (3,4,5))
A default value of None automatically causes a "promotion" of the type to a corresponding optional type. The following are equivalent:
def __init__(a: int = 1, b: float = 2.0, c : List[Int] = None):
def __init__(a: int = 1, b: float = 2.0, c : Optional[List[Int]] = None):

How to modify a class method?

I wish to modify the method of a class by changing its behaviour. Please note that I do NOT wish to rewrite another method altogether as it is complex and involves many variables and interacts with other methods. Here is an example of what I'm trying to do:
import types
class example_class(object):
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
self.pi = 3.14
def example_method(self):
print(self.a * self.b * self.c * self.pi)
#Create an instance of the object and call the method.
my_example = example_class(3, 5, 7)
my_example.example_method()
#Now I wish to change the "example_method" to add instead of multiply.
def different_method(self, a, b, c):
print(self.a + self.b + self.c + self.pi)
my_example.example_method() = types.MethodType(different_method(10,20,30), my_example)
I tried using types.MethodType but the above does not work. Note that I am trying to replace the example.method() with different.method(). I would like to give the method different values to calculate as well.
EDIT:
Thank you to all who answered my question! You have clarified it for me and now I can monkeypatch my classes! However, I should have clarified further. I wished to modify my method to include yet another variable. So my different_method should be like this:
#Now I wish to change the "example_method" to add instead of multiply.
def different_method(self, a, b, c, extra_variable):
print(self.a + self.b + self.c + extra_variable + self.pi)
I am having difficulty adding the extra variable-if you could provide some guidance on that, I’d be very grateful!
You're mixing up function objects, and the values returned by calls to those functions.
First, on the left side of the =, you have this:
my_example.example_method()
So you're not trying to replace the example_method attribute of my_example, you're trying to assign to whatever it returns (here, None). That makes no sense.
Then, on the right side, you're trying to build a method, not out of different_method, but out of the result of calling different_method(10,20,30). This also makes no sense.
What you want is:
my_example.example_method = types.MethodType(different_method, my_example)
Now you can call it the same as any other method:
my_example.example_method(10, 20, 30)
But, while we're at it, your different_method doesn't make much sense either. It takes parameters a, b, c, but completely ignores them, and instead just adds self.a + self.b + self.c. While this isn't illegal, it's kind of silly—and it means that the new method doesn't have the same signature as the one you were replacing, which is bound to cause confusion. So what you probably want is:
def different_method(self):
print(self.a + self.b + self.c + self.pi)
my_example.example_method = types.MethodType(different_method, my_example)
my_example.example_method()
If you want to add different values, you'd create a new example object, just as you would when you want to multiply different values:
my_other_example = example_class(10, 20, 30)
my_other_example.example_method = types.MethodType(different_method, my_other_example)
my_other_example.example_method()
If you plan to do a lot of this monkeypatching, you may want to write a function to patch example objects:
def patch_example(example, different_method):
example.example_method = types.MethodType(different_method, example)
… so you can do this:
my_third_example = example_class(1, 2.5, 0+1j)
patch_example(my_third_example)
my_third_example.example_method()
(However, if you really do need to do this multiple times, you probably wanted to create a subclass, or some other higher-level abstraction.)
# first you replace the method
my_example.example_method = types.MethodType(different_method, my_example)
# then you call it
my_example.example_method(10,20,30)

Python dynamically generate dependencies between class instances

Suppose I have the following classes:
class base(object):
def __init__(self, name):
self.name = name
self.last_x = 0.0
def calc(self, x):
return x
class A(base):
def calc(self, x):
return f_A(x)
class B(base):
def calc(self, x):
return f_B(x)
...
Each of the lettered classes is basically a wrapper for a corresponding lettered function f_A, f_B. The class instances include a state variable self.last_x as well as the lettered functions are assumed to be state-dependent (i.e. a Markov Chain type process).
What I would like to do is to define dependency chains between instances of these classes in order to try out different functional convolutions. For example, if we wanted to calculate a chain [a, b] on a numerical input value x we would have to do
a = A('firstnode')
b = B('secondnode')
res = b.calc(a.calc(x))
The goal is to do this with arbitrarily long chains, while also being able to access results from each intermediate calculation. I.e. if the chain is [a, b, c] I would like to make accessible results of [a] and [a, b] as well (which is why I included a name string for each node in my current implementation).
What would be the right way to setup my classes and data structures for this use case?
So far I have a fairly heavy-handed solution involving multiple dictionaries to keep track of things, but it feels inelegant and I think I might be missing out on something obvious.
Unfortunately you're improperly reusing names (thus hiding their previous values). E.g, after:
a = A('firstnode')
calling a.calc will try to call this instance (since the assignment has replaced the fact that previously name a was bound to a function) and fail. Best would be to use more sensible naming. If for some reason that's not practical, you need to bind the function names internally at class definition time:
class A(base):
def calc(self, x, a=a):
return a(x)
where the a=a does the trick, and so forth.
Having passed that hurdle, the second one is that you want the last result of each class to be saved, but, you don't save it. So, change the code to e.g
class A(base):
def calc(self, x, a=a):
self.last_result = a(x)
return self.last_result
Once that is done, performing your desired operation on a list of class instances is the least of your problems. E.g
def doit(instances, x):
curr = x
for inst in instances: curr=inst.calc(curr)
return curr
and after this
[inst.last_result for inst in instances]
will give you the intermediate results you're looking for.

Where is it better to declare variables within a Python class?

When I write a class, I declare a some variables within the __init__ method and some other in the other functions. So, I usually end up with something like this:
class Foo:
def __init__(self,a, b):
self.a=a
self.b=b
def foo1(self,c, d)
sum=self.a+self.b+c+d
foo2(sum)
def foo2(self,sum)
print ("The sum is ", sum)
I find this way a bit messy because it gets difficult to keep track of all variables. In contrary, managing variables when they are declared within the __init__ method becomes more easy. So, instead of the previous form, we would have:
class Foo:
def __init__(self,a, b, c, d, sum):
self.a=a
self.b=b
self.c=c
self.d=d
self.sum=sum
def foo1(self)
self.sum=self.a+self.b+self.c+self.d
foo2(self.sum)
def foo2(self)
print ("The sum is ", self.sum)
Which one would you choose and why? Do you think declaring all the variables of all functions of the class in the __init__ method would be a better practice?
Your sum is a good candidate for a computed value i.e. method that acts like class variable (not method). This can be done by #property
class Foo(object):
def __init__(self, a, b):
self.a = a
self.b = b
#property
def sum(self)
return self.a + self.b
f = Foo(1, 2)
print 'The sum is' + f.sum
http://docs.python.org/2/library/functions.html#property
There are at least three aspects to this:
From a design standpoint your constructor should only define variables, that are used at least in two methods in your class or that convey an essential characteristic of the thing you try to model.
From a performance point of view, you should use variables with the smallest scope possible, it saves lookups.
Keeping the variables local, keeps the cognitive load low.

Categories