Factory Reset of a class instance - python

I have a Python code whose structure is as following:
for i in range (0,N):
ClassInstance = A(params)
... #some operation that modify the attribute of the instance "ClassInstance" of the class "A"
A is a class linked to another one by a class composition relation.
Now I want to reset at each loop cycle the class' instance and:
I don't want to create a new instance for each cycle with a different name
I don't want to write a method in A for the manual reset of attributes since they are many and not all defined in the __init__ method but spreaded inside the various methods of the class.
I just want that at each cycle the same old instance assumes the same state that had just after its creation; to do so I'd proceed putting a reset method in A like the following:
def reset(self, params):
self = A(params)
and modify the structure of the code as follow:
ClassInstance = A(params)
for i in range (0,N):
ClassInstance.reset(params)
... #some operation that modify the attribute of the instance "ClassInstance" of the class "A"
Is it a safe way to lose track of the previous history of ClassInstance, restarting at each cycle from 0, or there is some cons that I'm not considering?
P.S.
Searching online I saw some previous similar post (as Preferred way of resetting a class in Python) but I'd like to understand if this specific way works and if I should be careful about something when proceeding by it.
Clearly if this method is a wrong way to solve my problem, other approches/solutions that fit my circumstance are well accepted as well (but in that case I'd like to understand where is the problem)

You have to understand what does the = operator when you assign to a variable like in a=obj
That does not modify the object which the variable was pointing at. That just set which object the variable will be referencing from then on, leaving the old object alone.
So ClassInstance = A() makes a new object and assign it to the variable ClassInstance
The old instance is not referenced anymore, and you cannot access it anymore. So in each loop you have a new fresh instance.
Also, you cannot reset an instance by doing self = A() You are assigning a new object to the local variable self. The instance which was referenced by self is not touched. Moreover, when the method returns, the self variable is released and the instance is not referenced anymore, so it is destroyed.
In general assigning to variables does not do anything. Is just like putting a label to an object. self is a variable like any other. There is nothing special about it.
The only way of resetting an instance is by manually setting its properties.
('object' and 'instance' in this context means the same)
By the way, you cannot in python actually destroy objects. You can delete variables with del a for example, but the objects are destroyed automatically when they are not referenced anymore.

Related

What is the difference between assign and declare a variable in python? [duplicate]

I want to clarify how variables are declared in Python.
I have seen variable declaration as
class writer:
path = ""
sometimes, there is no explicit declaration but just initialization using __init__:
def __init__(self, name):
self.name = name
I understand the purpose of __init__, but is it advisable to declare variable in any other functions?
How can I create a variable to hold a custom type?
class writer:
path = "" # string value
customObj = ??
Okay, first things first.
There is no such thing as "variable declaration" or "variable initialization" in Python.
There is simply what we call "assignment", but should probably just call "naming".
Assignment means "this name on the left-hand side now refers to the result of evaluating the right-hand side, regardless of what it referred to before (if anything)".
foo = 'bar' # the name 'foo' is now a name for the string 'bar'
foo = 2 * 3 # the name 'foo' stops being a name for the string 'bar',
# and starts being a name for the integer 6, resulting from the multiplication
As such, Python's names (a better term than "variables", arguably) don't have associated types; the values do. You can re-apply the same name to anything regardless of its type, but the thing still has behaviour that's dependent upon its type. The name is simply a way to refer to the value (object). This answers your second question: You don't create variables to hold a custom type. You don't create variables to hold any particular type. You don't "create" variables at all. You give names to objects.
Second point: Python follows a very simple rule when it comes to classes, that is actually much more consistent than what languages like Java, C++ and C# do: everything declared inside the class block is part of the class. So, functions (def) written here are methods, i.e. part of the class object (not stored on a per-instance basis), just like in Java, C++ and C#; but other names here are also part of the class. Again, the names are just names, and they don't have associated types, and functions are objects too in Python. Thus:
class Example:
data = 42
def method(self): pass
Classes are objects too, in Python.
So now we have created an object named Example, which represents the class of all things that are Examples. This object has two user-supplied attributes (In C++, "members"; in C#, "fields or properties or methods"; in Java, "fields or methods"). One of them is named data, and it stores the integer value 42. The other is named method, and it stores a function object. (There are several more attributes that Python adds automatically.)
These attributes still aren't really part of the object, though. Fundamentally, an object is just a bundle of more names (the attribute names), until you get down to things that can't be divided up any more. Thus, values can be shared between different instances of a class, or even between objects of different classes, if you deliberately set that up.
Let's create an instance:
x = Example()
Now we have a separate object named x, which is an instance of Example. The data and method are not actually part of the object, but we can still look them up via x because of some magic that Python does behind the scenes. When we look up method, in particular, we will instead get a "bound method" (when we call it, x gets passed automatically as the self parameter, which cannot happen if we look up Example.method directly).
What happens when we try to use x.data?
When we examine it, it's looked up in the object first. If it's not found in the object, Python looks in the class.
However, when we assign to x.data, Python will create an attribute on the object. It will not replace the class' attribute.
This allows us to do object initialization. Python will automatically call the class' __init__ method on new instances when they are created, if present. In this method, we can simply assign to attributes to set initial values for that attribute on each object:
class Example:
name = "Ignored"
def __init__(self, name):
self.name = name
# rest as before
Now we must specify a name when we create an Example, and each instance has its own name. Python will ignore the class attribute Example.name whenever we look up the .name of an instance, because the instance's attribute will be found first.
One last caveat: modification (mutation) and assignment are different things!
In Python, strings are immutable. They cannot be modified. When you do:
a = 'hi '
b = a
a += 'mom'
You do not change the original 'hi ' string. That is impossible in Python. Instead, you create a new string 'hi mom', and cause a to stop being a name for 'hi ', and start being a name for 'hi mom' instead. We made b a name for 'hi ' as well, and after re-applying the a name, b is still a name for 'hi ', because 'hi ' still exists and has not been changed.
But lists can be changed:
a = [1, 2, 3]
b = a
a += [4]
Now b is [1, 2, 3, 4] as well, because we made b a name for the same thing that a named, and then we changed that thing. We did not create a new list for a to name, because Python simply treats += differently for lists.
This matters for objects because if you had a list as a class attribute, and used an instance to modify the list, then the change would be "seen" in all other instances. This is because (a) the data is actually part of the class object, and not any instance object; (b) because you were modifying the list and not doing a simple assignment, you did not create a new instance attribute hiding the class attribute.
This might be 6 years late, but in Python 3.5 and above, you can give a hint about a variable type like this:
variable_name: type_name
or this:
variable_name # type: shinyType
This hint has no effect in the core Python interpreter, but many tools will use it to aid the programmer in writing correct code.
So in your case(if you have a CustomObject class defined), you can do:
customObj: CustomObject
See this or that for more info.
There's no need to declare new variables in Python. If we're talking about variables in functions or modules, no declaration is needed. Just assign a value to a name where you need it: mymagic = "Magic". Variables in Python can hold values of any type, and you can't restrict that.
Your question specifically asks about classes, objects and instance variables though. The idiomatic way to create instance variables is in the __init__ method and nowhere else — while you could create new instance variables in other methods, or even in unrelated code, it's just a bad idea. It'll make your code hard to reason about or to maintain.
So for example:
class Thing(object):
def __init__(self, magic):
self.magic = magic
Easy. Now instances of this class have a magic attribute:
thingo = Thing("More magic")
# thingo.magic is now "More magic"
Creating variables in the namespace of the class itself leads to different behaviour altogether. It is functionally different, and you should only do it if you have a specific reason to. For example:
class Thing(object):
magic = "Magic"
def __init__(self):
pass
Now try:
thingo = Thing()
Thing.magic = 1
# thingo.magic is now 1
Or:
class Thing(object):
magic = ["More", "magic"]
def __init__(self):
pass
thing1 = Thing()
thing2 = Thing()
thing1.magic.append("here")
# thing1.magic AND thing2.magic is now ["More", "magic", "here"]
This is because the namespace of the class itself is different to the namespace of the objects created from it. I'll leave it to you to research that a bit more.
The take-home message is that idiomatic Python is to (a) initialise object attributes in your __init__ method, and (b) document the behaviour of your class as needed. You don't need to go to the trouble of full-blown Sphinx-level documentation for everything you ever write, but at least some comments about whatever details you or someone else might need to pick it up.
For scoping purpose, I use:
custom_object = None
Variables have scope, so yes it is appropriate to have variables that are specific to your function. You don't always have to be explicit about their definition; usually you can just use them. Only if you want to do something specific to the type of the variable, like append for a list, do you need to define them before you start using them. Typical example of this.
list = []
for i in stuff:
list.append(i)
By the way, this is not really a good way to setup the list. It would be better to say:
list = [i for i in stuff] # list comprehension
...but I digress.
Your other question.
The custom object should be a class itself.
class CustomObject(): # always capitalize the class name...this is not syntax, just style.
pass
customObj = CustomObject()
As of Python 3, you can explicitly declare variables by type.
For instance, to declare an integer one can do it as follows:
x: int = 3
or:
def f(x: int):
return x
see this question for more detailed info about it:
Explicitly declaring a variable type in Python

Python attribute that holds other existing attributes in a list

I am currently working on a python-sqlite project, and i am novice to both.
I have created a class that has some attributes declared inside the __init__ method. I need another attribute that will be a list or array, that will contain some of the already declared attributes of the class. What i want is my list to contain just a reference of the original attributes.
I need this structure in order to be able to call these attributes together, to iterate on them, but i want to be able to call them separately, too.
At first I tried to create that list attribute inside the __init__ method, after the rest declarations. When I create an instance, however, and change the initial value of one of the attributes, the attribute in the list is not updated.
Then I tried to create that same list attribute inside another method of the class, instead of inside the init, and call it from inside my code, and it did what I wanted to.
Why does the different approach has different results?
Here is the code at both cases:
Case #1
class Tools():
def __init__(self):
self.name = "defaultname"
self.manufacturer = "defaultmanuf"
self.tooldetails = [self.name, self.manufacturer]
def get_details(self):
return self.tooldetails
Case #2
class Tools():
def __init__(self):
self.name = "defaultname"
self.manufacturer = "defaultmanuf"
def _set_detail_list(self):
self.tooldetails = [self.name, self.manufacturer]
def get_details(self):
_set_detail_list()
return self.tooldetails
And when I create an instance:
tool1 = Tools()
tool1.name = 'abc'
tool1.get_details()
The first case gives me ["defaultname", "defaultmanuf"] while the second gives me ["abc","defaultmanuf"].
My question is what is the reason python gives me different output for each case? It seems like I miss something important about how initialization is working..
dir() and other similar functions or magic methods could be able to give me what i want, but i think they are not flexible enough if you want many different lists with different sets of attributes. Unluckily, introspection doesn't work very well with sqlite string-formatted commands..
Plus i am curious of the way python works, which I believe is very important..
Thanks!!
Case #1
When your list is created within __init__, it contains pointers to 2 strings. But the link between name and tooldetails is irrevocably broken once your list is created. If you update name, tooldetails will not dynamically update, unless you tell Python to explicitly update tooldetails with new data.
Case #2
Here you explicitly tell Python to reconstruct tooldetails via the method _set_detail_list, which is called within get_details. You update name and then tell Python to rebuild the tooldetails list. Therefore, if you update name followed by get_details, your list will be updated.

Init in classes - is the first argument a stand in for the instance?

I have been trying to fully understand this for a while now, and practically speaking I think I understand what happens but I can't seem to find anywhere that confirms wether I understood it correctly:
class test(object):
def __init__(self, this):
self.something = this
example = test("writing")
My question is: In the above example, is it correct that self is simply a stand-in for the instance I am creating? Meaning that when i create an instance and assign it to "example", then "example is put in place of self and behind the scenes does something resembling this:
class test(object):
def __init__(example, this):
example.something = this
example = test("writing")
Furthermore, does that also mean that as long as I am still working with this on a class basis (say in tandem with another class) I should still be using self.something, while I should be using example.something if I am working with it on an instance level?
I hope that made somewhat sense, im still trying to wrap my head properly around all of it, so let me know if I need to try and rephrase it.
For reference sake, should someone else end up asking the same, this reply: Python __init__ and self what do they do? almost did the trick for me, and only really left me a bit in doubt about the above questions.
This is correct. self is the instance of the class (i.e. the object) and you use it inside the class code (inside it's methods).
While the first argument can be named something else (example in your second code), the convention is that we always use self or the code might be highly confusing for other programmers. But you got the gist right by doing that, the example variable in the class (i.e. the self in your first code) and the example variable outside of the class is basically the same thing.
By the way, I'd also avoid the following two things:
having a class name that starts with a small leter case,
using a variable name this (since a variable named this does in some other languages essentially what self does in Python).
In Python, variables do not "contain" objects, they refer to them. So:
class test(object):
def __init__(self, this):
self.something = this
example = test("writing")
In this case example is a reference to the new object, but so is self. It is perfectly legal, and common, to have multiple references to the same object.
If you did:
another = example
this would not create a new object but have another reference to the same object. another, example (and self) would be references to the same single object.
You can test this by looking at the object's unique identifier, using id(). Add:
another = example
print id(another)
print id(example)
you will find that their id's are the same.

How to write a class method in Python that captures the name of the instance and apply some action on it

I don't know if it's possible but here is what I want( or dream of...):
class A:
method1:
return something
method2:
return something else
method3:
return(some_action, the_name_of_instance)
more_methods:
In another words, if class A is instantiated as:
inst_a = A("some")
method3 should know the name of the instance(inst_a) by this time and it will apply some action defined by the user.
Therefore, from user point of view, the action is applied onto inst_a as soon as the method is called as in inst_a.method3()
This is not how python works - a single PyObject (an instance) might go by any number of names, and you can't thus put a single name on something:
my_instance = MyClass()
my_second_instance = my_instance # what's the "name" of that instance now? there's two handles for it, now.
Also, there's many cases where your instance doesn't have a name at all - for example, if it's just created "temporarily" when created to be directly passed to a function.
print pickle.dumps(MyClass()) # the new instance doesn't even get a long-lived name at all
All in all, I think you intend to break the isolation between the state of the object and the reference to that object -- which is almost certainly not a good idea. If you need a unique identifier, use the built-in id function: id(self).
EDIT https://stackoverflow.com/users/642070/tdelaney correctly pointed out that you have to be careful with id(), because you have no guarantee that after the end of life of one object, another object can't get the same id, which, by the way, in CPython is actually the memory address of the underlyin PyObject.

how do you return a property and save it as a global variable? (python)

here is my code:
def createProp(fget, fset, fdel):
class prop(object):
get=property(fget,fset,fdel)
return prop().get
x=createProp(getter,setter,deleter)
what I would like to do is have it return a property, so that I can make the variable x a property, but I can't figure out how to make a function like this. what it is doing instead is returning the value of the property, not the property itself.
python 2.6
createProp = property
Properties are perfectly ordinary objects. property is just another class. You can create an instance of it at runtime, in whatever context you want (no need for a class), pass them around, etc. And yes, you could (pointlessly) create one in a local class and then fetch it from there. But as the whole point of properties is overriding attribute access on instance, you'd have to fetch it from the class: return prop.get.
However, as property works its magic by means of the descriptor protocol, and descriptors have to be in a class to work. You can't have a global (or local) variable that's a property -- well, you can, but using it would just give you the descriptor object, not run fget, and assignment wouldn't trigger fset. You cannot do anything like that, and you shouldn't.

Categories

Resources