The problem in code:
class Object:
def __init__(self, x, y):
self.a = some_logic(x)
self.b = some_logic(y)
#classmethod
def produce_object(cls, x, y, additional_knowledge) -> 'Object':
a = some_different_logic(x, additional_knowledge)
b = some_different_logic(y, additional_knowledge)
# now how to initialize Object?
# bad ( I dont want to call some_logic):
o = Object(x, y)
o.a = x
o.b = y
return o
I want to create a new instance, that creates it in another way than the constructor does. For this alternative way I would maybe use some additional knowledge that allows me to call produce_object (which spares me a lot of expensive calculations located in the constructor).
In Java I would place the a and b instance variables outside the constructor in the body of the class.
If I do this in python, they are treated as static members.
Do i have to do something like this ?:
class Object:
def __init__(self):
self.a = None
self.b = None
def former_constructor(self, x, y):
self.a = some_logic(x)
self.b = some_logic(y)
def produce_object(self, x, y):
self.a = some_different_logic(x)
self.b = some_different_logic(y)
My best guess (since you cant have multiple constructors, right?) that one would write a very generic constructor. This of course leads to complicated code in the real world (imagine you have 3 ways to calculate your stuff. One takes arguments of type A and B, second C and D, third E and F. But you always have to provide everything (with potentially four Nones).
Any comment on this is very appreciatied. Thanks for you help.
The #classmethod approach can be modified to provide an alternative
constructor which does not invoke the default constructor (init).
Instead, an instance is created using new.
According to #Andrzej Pronobis
Related
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.
Usually, to refer to an instance variable, the variable name must be preceded with self, as in,
class A:
def __init__(self, x: int):
self.x = x
def print_x(self):
print(self.x)
However, I noticed that if the instance variable is an object, this is not neccesary. That is, I can do,
class B:
pass
class A:
def __init__(self, b: B):
pass
def print_b(self):
print(b)
b = B()
a = A(b)
a.print_b()
and calling print_b from an A object will print the memory address of the B object, without raising an error.
Is this equivalent to explicitly declaring b to be an instance variabe, via self.b = b in __init__ and referring to b as self.b thereafter? And if so, is this proper convention?
And if so, is this proper convention?
In both situations, the design may suffer of tight coupling. The general rule of thumb is to always depend on abstractions, not on concretions.
Only if you share with us the actual code (MCVE) and provide some context then we could provide a practical and better solution to your problem.
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)
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.
Edit: There was some confusion, but I want to ask a general question about object oriented design in Python.
Consider a class that lets you map data values to counts or frequencies:
class DataMap(dict):
pass
Now consider a subclass that allows you to construct a histogram from a list of data:
class Histogram(DataMap):
def __init__(self, list_of_values):
# 1. Put appropriate super(...) call here if necessary
# 2. Build the map of values to counts in self
pass
Now consider a class that lets you make a smoothed probability mass table rather than a Histogram.
class ProbabilityMass(DataMap):
pass
What is the best way to allow a ProbabilityMass to be constructed from either a Histogram or a list of values?
I "grew up" programming in C++, and in this case I would use an overloaded constructor. In Python I've thought of doing this with:
The constructor takes multiple arguments (all but one of these should == None)
I define from_Histogram and from_list methods
In the second case (which I believe is better), what is the best way to allow the from_list method to use the shared code from the Histogram constructor? A ProbabilityMass table is nearly identical to a Histogram table, but it is scaled so that the sum of all value is 1.0.
If you have come across a similar problem, please share your expertise!
To start with, if you think you want #staticmethod, you almost always don't. Either the function is not part of the class, in which case it should just be a free function, or it is part of the class, but not tied to an instance, and it should be a #classmethod. Your named constructor is a good candidate for a #classmethod.
Also note that you should invoke A.__init__ from B via super(), otherwise multiple inheritance can bite you bad.
class A:
def __init__(self, data):
self.values_to_counts = {}
for val in data:
if val in self.values_to_counts:
self.values_to_counts[val] += 1
else:
self.values_to_counts[val] = 1
#classmethod
def from_values_to_counts(cls, values_to_counts):
self = cls([])
self.values_to_counts = values_to_counts
return self
class B(A):
def __init__(self, data, parameter):
super(B, self).__init__(data)
self.parameter = parameter
def print_parameter(self):
print self.parameter
In this case, you don't need a B.from_values_to_counts, it inherits from A, and it will return an instance of B, since that's how it was called.
If you need to do more complex initialization in B, you can, using super(), which looks very similar to the way it would when you use it with instances. after all, a classmethod really isn't anything more complex than an instancemethod where the im_self attribute is assigned to the class itself.
class A:
def __init__(self, data):
self.values_to_counts = {}
for val in data:
if val in self.values_to_counts:
self.values_to_counts[val] += 1
else:
self.values_to_counts[val] = 1
#classmethod
def from_values_to_counts(cls, values_to_counts):
self = cls([])
self.values_to_counts = values_to_counts
return self
class B(A):
def __init__(self, data, parameter):
super(B, self).__init__(data)
self.parameter = parameter
def print_parameter(self):
print self.parameter
#classmethod
def from_values_to_counts(cls, values_to_counts):
self = super(B, cls).from_values_to_counts(values_to_counts)
do_more_initialization(self)
return self