After using Django for a while, I got use to using classes without def __init__(self): ... when declaring variables. I used to declare my variables in the __init__ function, I now realize that there are cases where don't need to, I'm just unclear on when to use it or not. It seems there is a problem when trying to pass a class to a variable, should I use init in these cases?
I know I could just use __init__ for all cases, but it just makes my short classes like cleaner without it, so I would like to know when I can and cannot use it.
example:
class BaseScraper(object):
# whithout __init__, passing Site() to site wont work.
# site = Site()
# parser = None
def __init__(self):
self.site = Site()
self.parser = None
class Site(object):
# no trouble declaring url as a str
url = ""
def set(self, url):
self.url = url
def get(self):
return self.url
if __name__ == "__main__":
scraper = BaseScraper()
scraper.site.set('http://www.google.com')
print scraper.site.get()
Attributes declared in the class are owned by the class rather than by individual instances of the class. In your site example, url is no more a property of individual Site objects than set or get are. For this kind of example, you want instance data - which you can initialize in __init__.
Python: Difference between class and instance attributes has a good discussion of the differences.
This fails because Site class is not defined yet.
And (as #Peter DeGlopper) said, there is a big difference between class variables and instance variables.
class BaseScraper(object):
# This fails!
site = Site()
parser = None
class Site(object):
# no trouble declaring url as a str
url = ""
def set(self, url):
self.url = url
def get(self):
return self.url
When the virtual machine compile a python module, read and compile everything in class declaration, but on method declaration (like def __init__(...):) only read this line, ignoring the method body.
Example:
class Foo(object):
bar1 = "bar"
foo1 = "foo"
def __init__(self):
self.bar2 = "BAZ"
foo = Foo #Put a class in a veriable? yes, you can.
foo.bar1 # returns "bar"
foo.foo1 # returns "foo"
foo.bar2 # fails!!!! This will be a instance variable, but doesn't exist yet
foo2 = Foo() # Here the __init__ is called
foo2.bar2 # returns "BAZ"
foo2.bar1 #Returns "bar" because all class variables are availables from instances
Hope this helps =)
Related
I want a program to call a specific class based on a parameter/variable value. However, I don't want to use any clunky if-statements. My first thought was to use the globals() function, but I couldn't get it to work. Here's an example:
class SomeClass:
def __init__():
print("Hello, world!")
class OtherClass:
def runClass(className):
# Call class based on variable className
The reason I want to do this is because there is a wide variety of classes may need to be called, and so just piling up if-statements in my code won't do it. Any help would be greatly appreciated. Thanks!
Here's how you can call a class via globals
class SomeClass:
def __init__(self):
print("Hello, world!")
def __call__(self):
return "SomeClass called"
class OtherClass:
def runClass(self, className):
globals()[className]()()
o = OtherClass()
result = o.runClass("SomeClass")
print(result)
Notice, I am instantiating and then calling it via the __call__ special method, which is the closest match to your description I could think of.
Use a dict.
name_to_class = dict(some=SomeClass,
other=OtherClass)
def factory(name):
klass = name_to_class(name)
return klass()
some_obj = factory("some")
other_obj = factory("other")
One way to solve this problem is to use a dictionary to map the values of the variable className to the corresponding class.
Try this exemple :
class SomeClass:
def init(self):
print("Hello, world!")
class OtherClass:
def init(self):
print("Goodbye, world!")
classNameToClass = {
"SomeClass": SomeClass,
"OtherClass": OtherClass
}
def runClass(className):
# Call class based on variable className
cls = classNameToClass[className]
return cls()
runClass("SomeClass") # prints "Hello, world!"
runClass("OtherClass") # prints "Goodbye, world!"
Here, the dictionary classNameToClass maps the string names of the classes (e.g. "SomeClass") to the corresponding class objects (e.g. SomeClass). Then, in the runClass function, we look up the class object using the value of the className variable, and call it to create an instance of the class.
I've found an answer. The parameter that governs the called class can just be assigned elsewhere. At first, I thought it would need some complex function, but in reality, I guess the question didn't give enough details. The class itself only uses items from whatever object is given. So, instead of having to dynamically call a class, it's as simple as:
class SomeClass:
def printHelloWorld():
print("Hello, world!")
class OtherClass:
def __init__(self, usingClass):
self.object = usingClass
def doThis():
usingClass.printHelloWorld()
x = OtherClass(SomeClass())
x.doThis()
It's on me for not giving enough information. Thank you all for your help.
I have a class called resources and I have defined one method called get_connect. I want to use the data of which get_connect returns to the other classes. I need at least three classes and I use the data of get_connect and I have to parse that data. To implement this I have written the code below
class resources:
#staticmethod
def get_connect():
return 1 + 2
class Source1(resources):
def __init__(self):
self.response = resources.get_connect()
def get__details1(self):
print(self.response)
class Source2(resources):
def __init__(self):
self.response = resources.get_connect()
def get_details2(self):
print(self.response)
class Source3(resources):
def __init__(self):
self.response = resources.get_connect()
def get__detail3(self):
print(self.response)
source1 = Source1()
source2 = Source2()
source3 = Source3()
source1.get__details1()
source2.get_details2()
source3.get__detail3()
But the problem with the code is for every class in init method I am calling the get_connect method. I don't want to repeat the code. I need help for avoiding redundancy which I have asked below
Is there any way I can call get_connect in one place and use it for other classes maybe a decorator or anything? if yes how can I?
While creating objects also I am calling each class and calling each method every time. is there a way to use any design pattern here?
If anyone helps me with these oops concepts it will be useful.
First of all, is there any reason why you are using get_connect method as static?
Because what you can do here is declare it in the parent class:
class resources:
def __init__(self):
self.response = self.get_connect()
def get_connect(self):
return 1 + 2
This way you do not need to define the __init__ method on every class, as it will be automatically inherited from the parent.
Regarding the second question, it really depends on the context, but you can use a strategy pattern in order to retrieve the class that you require to call. For this rename the method of get details into the same for each of the classes, as basically they're used for the same purpose, but changed on the context of the class implementation:
class Source1(resources):
def get_details(self):
print(self.response)
class Source2(resources):
def get_details(self):
print(self.response)
class Source3(resources):
def get_details(self):
print(self.response)
classes = {
"source_1": Source1,
"source_2": Source2,
"source_3": Source3
}
source_class = classes["source_1"]
source = source_class()
source.get_details()
Hope this helped!
I have a class MyClass with a complex __init__ function.
This class had a method my_method(self) which I would like to test.
my_method only needs attribute my_attribute from the class instance.
Is there a way I can mock class instances without calling __init__ and by setting the attributes of each class instance instead?
What I have:
# my_class.py
from utils import do_something
class MyClass(object):
def __init__(self, *args, **kwargs):
# complicated function which I would like to bypass when initiating a mocked instance class
pass
def my_method(self):
return do_something(self.my_attribute)
What I tried
#mock.patch("my_class.MyClass")
def test_my_method(class_mock, attribute):
instance = class_mock.return_value
instance.my_attribute = attribute
example_instance = my_class.MyClass()
out_my_method = example_instance.my_method()
# then perform some assertions on `out_my_method`
however this still makes usage of __init__ which I hope we can by-pass or mock.
As I mentioned in the comments, one way to test a single method without having to create an instance is:
MyClass.my_method(any_object_with_my_attribute)
The problem with this, as with both options in quamrana's answer, is that we have now expanded the scope of any future change just because of the tests. If a change to my_method requires access to an additional attribute, we now have to change both the implementation and something else (the SuperClass, the MockMyClass, or in this case any_object_with_my_attribute_and_another_one).
Let's have a more concrete example:
import json
class MyClass:
def __init__(self, filename):
with open(filename) as f:
data = json.load(f)
self.foo = data.foo
self.bar = data.bar
self.baz = data.baz
def my_method(self):
return self.foo ** 2
Here any test that requires an instance of MyClass. is painful because of the file access in __init__. A more testable implementation would split apart the detail of how the data is accessed and the initialisation of a valid instance:
class MyClass:
def __init__(self, foo, bar, baz):
self.foo = foo
self.bar = bar
self.baz = baz
def my_method(self):
return self.foo ** 2
#classmethod
def from_json(cls, filename):
with open(filename) as f:
data = json.load(f)
return cls(data.foo, data.bar, data.baz)
You have to refactor MyClass("path/to/file") to MyClass.from_json("path/to/file"), but wherever you already have the data (e.g. in your tests) you can use e.g. MyClass(1, 2, 3) to create the instance without requiring a file (you only need to consider the file in the tests of from_json itself). This makes it clearer what the instance actually needs, and allows the introduction of other ways to construct an instance without changing the interface.
There are at least two options I can see:
Extract a super class:
class SuperClass:
def __init__(self, attribute):
self.my_attribute = attribute
def my_method(self):
return do_something(self.my_attribute)
class MyClass(SuperClass):
def __init__(self, *args, **kwargs):
super().__init__(attribute) # I don't know where attribute comes from
# complicated function which I would like to bypass when initiating a mocked instance class
Your tests can instantiate SuperClass and call my_method().
Inherit from MyClass as is and make your own simple __init__():
class MockMyClass(MyClass):
def __init__(self, attribute):
self.my_attribute = attribute
Now your test code can instantiate MockMyClass with the required attribute and call my_method()
For instance, you could write the test as follows
def test_my_method(attribute):
class MockMyClass(MyClass):
def __init__(self, attribute):
self.my_attribute = attribute
out_my_method = MockMyClass(attribute).my_method()
# perform assertions on out_my_method
I have defined a python context class and a Test class in a file:
class Test(object):
pass
class MyContext(object):
def __init(self):
self._vars = []
def __enter__(self):
pass
def __exit(self, ....):
pass
In another file using that context:
from somewhere import Test, MyContext
with MyContext() as ctx:
mytest = Test()
So what I want to achieve is that when I exit the context, I want to be aware of the mytest instance created and add it in the ctx._vars = [<instance of Test >].
I don't want to have a ctx.add_var(mytest) method, I want those Test instances to be added automatically to the ctx instance.
That is possible of being done, using Python's introspection capabilities, but you have to be aware this is not what the with context block was created for.
I agree it is a useful syntax construction that can be "deviated" to do things like what you want: annotate the objects created inside a code block in a "registry".
Before showing how to do that with a context manager consider if a class body would not suffice you. Using a class body this way also deviates from its primary purpose, but you have your "registry" for free:
from somewhere import Test, MyContext
class ctx:
mytest = Test()
vars = ctx.__dict__.values()
In order to do that with a context manager, you have to inspect the local variables at the start and at the end of the with block. While that is not hard to do, it wuld not cover all instances of Test created - because if the code is like this:
mytests = []
with Mycontext as ctx:
mytests.append(Test())
No new variable is created - so code tracking the local variables would not find anything. Code could be written to look recursively into variables with containers, such as dictionaries and lists - but then mytest() instances could be added to a container referenced as a global variable, or a variable in other module.
It turns out that a reliable way to track Test instances would be to instrument the Test class itself to annotate new instances ina registry. That is far easier and less depentend on "local variable introspection" tricks.
The code for that is somewhat like:
class Test(object):
pass
class MyContext(object):
def __init(self, *args):
self.vars = []
self.track = args
self.original_new = {}
def patch(self, cls_to_patch):
cls_new = getattr(cls_to_patch, "__new__")
if "__new__" in cls.__dict__:
self.original_new[cls_to_patch] = cls_new
def patched_new(cls, *args, **kwargs):
instance = cls_new(*args, **kwags)
self.vars.append(instance)
return instance
cls_to_patch.__new__ = patched_new
def restore(self, cls):
if cls in self.original_new:
# class had a very own __new_ prior to patching
cls.__new__ = self.original_new[cls]
else:
# just remove the wrapped new method, restores access to superclass `__new__`
del cls.__new__
def __enter__(self):
for cls in self.track:
self.patch(cls)
return self
def __exit(self, ....):
for cls in self.track:
self.restore(cls)
...
from somewhere import Test, MyContext
with MyContext(Test) as ctx:
mytest = Test()
This question already has answers here:
How can I access "static" class variables within methods?
(6 answers)
Closed 12 days ago.
Suppose I have this code:
class Example(object):
def the_example(self):
itsProblem = "problem"
theExample = Example()
print(theExample.itsProblem)
When I try it, I get an error that says:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Example' object has no attribute 'itsProblem'
How do I access this attribute? I tried adding another method to return it:
def return_itsProblem(self):
return itsProblem
but the problem persists.
The answer, in a few words
In your example, itsProblem is a local variable.
Your must use self to set and get instance variables. You can set it in the __init__ method. Then your code would be:
class Example(object):
def __init__(self):
self.itsProblem = "problem"
theExample = Example()
print(theExample.itsProblem)
But if you want a true class variable, then use the class name directly:
class Example(object):
itsProblem = "problem"
theExample = Example()
print(theExample.itsProblem)
print (Example.itsProblem)
But be careful with this one, as theExample.itsProblem is automatically set to be equal to Example.itsProblem, but is not the same variable at all and can be changed independently.
Some explanations
In Python, variables can be created dynamically. Therefore, you can do the following:
class Example(object):
pass
Example.itsProblem = "problem"
e = Example()
e.itsSecondProblem = "problem"
print Example.itsProblem == e.itsSecondProblem
prints
True
Therefore, that's exactly what you do with the previous examples.
Indeed, in Python we use self as this, but it's a bit more than that. self is the the first argument to any object method because the first argument is always the object reference. This is automatic, whether you call it self or not.
Which means you can do:
class Example(object):
def __init__(self):
self.itsProblem = "problem"
theExample = Example()
print(theExample.itsProblem)
or:
class Example(object):
def __init__(my_super_self):
my_super_self.itsProblem = "problem"
theExample = Example()
print(theExample.itsProblem)
It's exactly the same. The first argument of ANY object method is the current object, we only call it self as a convention. And you add just a variable to this object, the same way you would do it from outside.
Now, about the class variables.
When you do:
class Example(object):
itsProblem = "problem"
theExample = Example()
print(theExample.itsProblem)
You'll notice we first set a class variable, then we access an object (instance) variable. We never set this object variable but it works, how is that possible?
Well, Python tries to get first the object variable, but if it can't find it, will give you the class variable. Warning: the class variable is shared among instances, and the object variable is not.
As a conclusion, never use class variables to set default values to object variables. Use __init__ for that.
Eventually, you will learn that Python classes are instances and therefore objects themselves, which gives new insight to understanding the above. Come back and read this again later, once you realize that.
You are declaring a local variable, not a class variable. To set an instance variable (attribute), use
class Example(object):
def the_example(self):
self.itsProblem = "problem" # <-- remember the 'self.'
theExample = Example()
theExample.the_example()
print(theExample.itsProblem)
To set a class variable (a.k.a. static member), use
class Example(object):
def the_example(self):
Example.itsProblem = "problem"
# or, type(self).itsProblem = "problem"
# depending what you want to do when the class is derived.
If you have an instance function (i.e. one that gets passed self) you can use self to get a reference to the class using self.__class__
For example in the code below tornado creates an instance to handle get requests, but we can get hold of the get_handler class and use it to hold a riak client so we do not need to create one for every request.
import tornado.web
import riak
class get_handler(tornado.web.requestHandler):
riak_client = None
def post(self):
cls = self.__class__
if cls.riak_client is None:
cls.riak_client = riak.RiakClient(pb_port=8087, protocol='pbc')
# Additional code to send response to the request ...
Implement the return statement like the example below! You should be good. I hope it helps someone..
class Example(object):
def the_example(self):
itsProblem = "problem"
return itsProblem
theExample = Example()
print theExample.the_example()
If you have a #classmethod static method, you always have the class as the first parameter:
class Example(object):
itsProblem = "problem"
#classmethod
def printProblem(cls):
print(cls.itsProblem)
Example.printProblem()