Could anybody explain whether it is safe to reinitialize an object by calling "self.init(". as shown in the following simplified example?
The reason i'm asking is that i couldn't find this method neither in several python books nor in internet. There are some who suggest to list all attributes and set them to initial value one by one. Basically i want to set my object to initial state after it has finished some tasks.
class Book(object):
def __init__(self,name,author):
self.name = name
self.author = author
self.copies = 5
def reset(self):
self.__init__(self.name,self.author)
def incrementCopy(self):
self.copies += 1
Kite = Book('kite runner','khaled hosseini')
print 'initial number of copies:', Kite.copies
Kite.incrementCopy()
Kite.incrementCopy()
Kite.incrementCopy()
print '3 copies are added:', Kite.copies
Kite.reset()
print 'number of copies are reinitialized', Kite.copies
initial number of copies: 5
3 copies are added: 8
number of copies are reinitialized 5
The only thing special about __init__ is that it is called automatically when an instance is created. Other than that it is a normal method, and it is safe to use it to set your object back to its initial state.
That being said, just because it is safe doesn't mean it is a good idea. Other people looking at your code might be confused by it, and it isn't difficult to do everything in a reset method (that __init__ can even call) to be more explicit about how the method is being used.
I would consider it a very bad practice - you should not __init__ manually (unless calling __init__ of the parent class). Also, passing object's data back to __init__ is somewhat strange.
Why not something like this:
class Book(object):
def __init__(self,name,author):
self.name = name
self.author = author
self.reset()
def reset(self):
self.copies = 5
I consider is not unsafe, I have used it and nothing strange happens in the memory, but take into account that attributes defined in other methods will not be reset. Consider for example:
class Dummy:
def __init__(self):
self.x = 4
def plus_one(self):
self.x += 1
def define_other_variables(self):
self.y = 3
def reset(self):
self.__init__()
D = Dummy()
print(D.x) # 4
# print(D.y) will raise an AttributeError
D.plus_one()
D.plus_one()
# D.y do not exist
D.define_other_variables()
# D.y exist
print(D.x) # 6
D.reset()
print(D.x) # 4
print(D.y) # 3, still exist!!
Then, just remember to define every object in the init function. you could consider bad practice for this reason but I still think is elegant.
Related
I am attempting to experiment with classes so I can better understand what they do. I wanted to build a counter which records the number of instances of a class (MyClass):
class ObjectCounter: # I want this to count the number of objects in each class
myclass_obj_count = 0
class MyClass(ObjectCounter):
def __init__(self):
super().myclass_obj_count += 1 # AttributeError: 'super' object has no attribute 'myclass_obj_count'
m1 = MyClass()
m2 = MyClass()
m3 = MyClass()
print(ObjectCounter.myclass_obj_count)
Since that didn't work, I looked online for someone trying to do the same thing. Here is some code I found online. This works as expected, and I feel like I have a basic understanding of how this works. This is a better solution to the task I was attempting, but I'm not satisfied because I want to know how super() works.
class geeks:
counter = 0
def __init__(self):
geeks.counter += 1
g1 = geeks()
g2 = geeks()
g3 = geeks()
print(geeks.counter) # this gives an expected result
Therefore, I tried this instead:
class ObjectCounter: # I want this to count the number of objects in each class
myclass_obj_count = 0
def add_myclass(self):
self.myclass_obj_count += 1
class MyClass(ObjectCounter):
def __init__(self):
super().add_myclass()
my_class_1 = MyClass()
my_class_2 = MyClass()
my_class_3 = MyClass()
print(ObjectCounter.myclass_obj_count) # expected output: 3
Instead of getting the expected output of 3, I got an output of 0. Why is this happening?
First, be aware of the += operator; it's implementation is quite subtle:
a += b
becomes
a = a.__iadd__(b)
This perhaps strange definition allows python to support it even for immutable types (like strings).
Note what happens when used for a class variable that is referred to by the alias self
class ObjectCounter: # I want this to count the number of objects in each class
myclass_obj_count = 0
def add_myclass(self):
self.myclass_obj_count += 1
# effectively becomes:
# self.myclass_obj_count = self.myclass_obj_count.__iadd__(1)
This will introduce an instance variable of the same name, shadowing the class variable.
You don't even need the subclass to test this:
>>> x = ObjectCounter()
>>> x.add_myclass()
>>> x.add_myclass()
>>> x.add_myclass()
>>> x.myclass_obj_count
3
>>> ObjectCounter.myclass_obj_count
0
Referring to the class variable directly instead of using self fixes this
def add_myclass(self):
ObjectCounter.myclass_obj_count += 1
I'm hesitant to give definite answers of what happens under the hood when class variables, super() and assignments are used, other than it just doesn't work. Perhaps because it would be quite ambiguous of whether or not we are defining class variables or new instance variables.
super() won't let you assign to either;
class ObjectCounter:
myclass_obj_count = 0
def __init__(self):
self.x = 'test'
class MyClass(ObjectCounter):
def __init__(self):
super().__init__()
print(super().myclass_obj_count) # reading works just fine
print(type(super())) # this isn't actually exactly the same as "ObjectCounter"
super().myclass_obj_count = 123 # no good
super().x = 'foo' # also no good.
All in all, for any assignment to class variables you can use the class name itself.
I will get straight to the point. I have been trying to find different ways in which I can check if an instance attribute exists in a method of a class which has not been called from the instance of the class.
Consider the following example:
class Main:
def thing1(self):
self.x = 10
m = Main()
print(m.x)
This code above will not work, until I call the method, e.g:
class Main:
def thing1(self):
self.x = 10
m = Main()
m.thing1()
print(m.x)
or
class Main:
def __init__(self):
self.thing1()
def thing1(self):
self.x = 10
m = Main()
print(m.x)
I want to know if there is a way I can check if this instance attribute exists in the method without having to call it.
I have tried using:
hasattr()
dir()
__ dict __
None of them will show that I have a instance attribute called 'x' in thing1
As well as this, I would greatly appreciate it if you could show me how I could check if an instance attribute in a method exists through another method.
class Main:
def thing1(self):
self.x = 10
def check(self):
# Is there a way I can check if 'x' exists in the method names thing1, without having to call it.
m = Main()
Thank you for taking the time to read this question, and I hope that you have a great day!
Indeed hasattr() definitely works. See interactive example below:
>>> class Main:
... def thing1(self):
... self.x = 10
...
>>> m = Main()
>>> hasattr(m, 'x')
False
>>> m.thing1()
>>> hasattr(m, 'x')
True
>>> print(m.x)
10
It's worth noting, though, that attributes should be initialized in the constructor. It's good practice to use __slots__ to declare attributes, but they must still be initialized. Also, class variables (attributes) are initialized outside the constructor. See example below:
class Main:
c = 5
__slots__ = ('x', 'y')
def __init__(self):
self.y = 12
def thing1(self):
self.x = 10
m = Main()
print(hasattr(m, 'x'))
print(hasattr(m, 'y'))
print(m.c)
Output:
False
True
5
After reading the question again and additional comments, it seems that you want to access an attribute that is initalized in a method, without actually calling said method. This is not possible because the attribute won't actually exist until it is initialized.
The only way I can think of to determine if the attribute would exist after the method call, is to read the code of the function in your program and check for the presence of the attribute. This is one crude example, which by no means is guaranteed to always work:
import inspect
lines = inspect.getsource(Main.thing1)
print('self.x' in lines)
The above, would print True for the code in question. I cannot think of a use case where this would be useful and strongly discourage it, but it's good to know the capabilities of Python.
Imagine the following code (which is totally useless if taken alone):
# define a property with additional methods
class P(property):
def __init__(self, name):
property.__init__(self,
fget=lambda self: self._get(name),
fset=lambda self, v: self._set(name, v))
self._name = name
def some_fn(self):
print('name: ' + self._name)
# define a class with two 'enhanced' properties
class C:
p1 = P('p1')
p2 = P('p2')
def __init__(self):
self._values = {}
def _get(self, name):
return self._values[name]
def _set(self, name, v):
self._values[name] = v
c = C()
c.p1 = 5
c.p2 = c.p1
print(c.p1, c.p2)
I just create a class C with two properties which have an extra method some_fn().
The problem is now: you can't call some_fn() easily by just writing c.p1.some_fn() because you would evaluate c.p1 first, which results in some value which doesn't provide the method any more.
I've tried to find some workarounds / approaches for calling some_fn in the context of a certain property, not it's value but I'm not happy yet.
My goal is quite simple:
I want to be able read/assign properties without boilerplate:
c.p1 = c.p2 instead of c.p1.set(c.p2.get())
The way I call the extra method/function must be easy to read/write
I want to write code that can be statically verified by pylint, mypy etc. so some_fn('c.p1') is not an option because it can't be checked whether 'c.p1' is a valid attribute of an existing objectc`.
some_fn doesn't have to be a method. It can be a function or any other way to request functionality in context of a property
I don't even need real properties. Any other way to write s.th.
like c.p1 == c.p2 (e.g. using __getattr__/__setattr__) would be fine, too as long as the get/set operations are still trackable.
I collected some code to make clear, what I'm talking about:
# ==== What I want to do ==============
c.p1.some_fn() # <-- this is what I want to write but
# it's invalid since it evaluates to
# 5.some_fn()
some_fn(c.p1) # <-- something like this looks OK, too but
# it evalueates to some_fn(5) (useless)
# ==== These are options that came to mind but I'm not happy with ======
getattr(C, 'p1').some_fn() # <-- this works but it is ugly
some_fn("c.p1") # <-- this is possible, too but I can't
# check integrity statically (pylint/mypy)
c.p1.value = c.p2.value # <-- this is a valid approach but it
c.p1.some_fn() # increases
some_fn(c.p1) # (again) # <-- This can acutally work if you `inspect`
# the call stack inside `C._get()` but
# it's black magic and incredibly slow
with some_fn(): # <-- this can work when `some_fn` changes
c.p1 # some global state which get's evaluated
# inside `C._get()`
My goal is quite simple: I want to be able read/assign properties without boilerplate: c.p1 = c.p2
If that is the goal here, it sounds like you've misunderstood properties, because they already work like that.
class C(object):
#property
def p1(self):
# get value
#p1.setter
def p1(self, val):
# set value
#property
def p2(self):
# get value
#p2.setter
def p2(self, val):
# set value
Then if you have an object c = C(), you can do c.p1 = c.p2, and it'll just work. Sticking more methods onto a property object is the wrong way to go.
If you really want to stick methods onto properties, retrieve the property through the class:
C.p1.some_fn()
I'm trying to use a function of a class object to create a new class object and running into problems. Here's the code I have so far:
class Room(object):
def __init__(self, name):
self.name = name
self.N = None
self.E = None
self.S = None
self.W = None
'''relevant code'''
def north(self,room):
self.N = Room(room)
self.N.S = self
def south(self,room):
self.S = Room(room)
self.S.N = self
So I want at least one of these print statements
room1 = Room('room1')
room1.north('room2')
print(room2.S)
print(Room(room2).S)
print(Room('room2').S)
to spit out 'room1', but the first two don't work because room2 as a variable is yet to be defined, and the last one doesn't work because it seems to be creating a new object instead of referencing the existing one, so it just prints the default 'None'.
Does there actually exist a way to reference an existing object with no variable set to it? Or is my only option to do something like this?
def north(self,room):
roomDict[room] = Room(room)
self.N = roomDict[room]
self.N.S = self
Edit: I realize I should probably be calling the new Room's south() function instead of directly changing the S variable, but that seems intuitively like it would cause a loop so I haven't touched it yet.
* Edited based on OP's clarification *
If you have a large number of objects you want to refer to without binding them to variables, dict is the way to go.
You can use #Berci's solution. But note that with that solution, if you already have a room named foo, you can't overwrite it by simply calling Room('foo') again -- doing that will just return the original foo room. To overwrite an existing room you must first do del Room.roomDict['foo'], and then call Room('foo'). This may be something you want, but maybe not.
The implementation below is less fanciful and doesn't require __new__ (in fact, Berci's solution doesn't need __new__ either and can be all done in __init__):
class Room:
registry = {}
def __init__(self, name):
self.registry[name] = self
# the rest of your __init__ code
If you want rooms to be non-overwritable, as they are in Berci's solution, just add two lines:
class Room:
registry = {}
def __init__(self, name):
if name in self.registry:
raise ValueError('room named "{}" already exists'.format(name))
self.registry[name] = self
It's not necessary to nest registry inside Room. You can make it an external dict if you want. The advantage of having the registry as a class attribute is that your Room object can access it as self.registry without knowing its global name. The (slight) disadvantage is that you need to type Room.registry or someroom.registry instead of just, say, registry, every time you access it.
Your dict solution can be brought to work. Use a class level roomDict and a new constructor not to create an already existing object referred by its name:
class Room(object):
roomDict = {}
def __new__(cls, name):
if name in cls.roomDict:
return cls.roomDict[name]
self = object.__new__(cls, name) # here the object is created
cls.roomDict[name] = self
return self
def __init__(self, name):
...
So that you can refer to room2 as Room('room2') afterwards.
A descriptor class is as follows:
class Des(object):
def __get__(self, instance, owner): ...
def __set__(self, instance, value): ...
def __delete__(self, instance): ...
class Sub(object):
attr = Des()
X = sub()
Question
I don't see the point of the existence of owner, how can I use it?
To make an attr read-only, we shouldn't omit __set__ but define it to catch the assignments and raise an exception. So X.attr = 123 will fail, but __set__'s arguments doesn't contain owner, which means I can still do Sub.attr = 123, right?
See http://docs.python.org/reference/datamodel.html#implementing-descriptors:
owner is always the owner class, while instance is the instance that the attribute was accessed through, or None when the attribute is accessed through the owner
A case where you would use owner would be creating a classproperty:
class _ContentQueryProperty(object):
def __get__(self, inst, cls):
return Content.query.filter_by(type=cls.TYPE)
You can experiment with this example:
# the descriptor protocol defines 3 methods:
# __get__()
# __set__()
# __delete__()
# any class implementing any of the above methods is a descriptor
# as in this class
class Trace(object):
def __init__(self, name):
self.name = name
def __get__(self, obj, objtype):
print "GET:" + self.name + " = " + str(obj.__dict__[self.name])
return obj.__dict__[self.name]
def __set__(self, obj, value):
obj.__dict__[self.name] = value
print "SET:" + self.name + " = " + str(obj.__dict__[self.name])
# define the attributes of your class (must derive from object)
# to be references to instances of a descriptor
class Point(object):
# NOTES:
# 1. descriptor invoked by dotted attribute access: A.x or a.x
# 2. descripor reference must be stored in the class dict, not the instance dict
# 3. descriptor not invoked by dictionary access: Point.__dict__['x']
x = Trace("x")
y = Trace("y")
def __init__(self, x0, y0):
self.x = x0
self.y = y0
def moveBy(self, dx, dy):
self.x = self.x + dx # attribute access does trigger descriptor
self.y = self.y + dy
# trace all getters and setters
p1 = Point(15, 25)
p1.x = 20
p1.y = 35
result = p1.x
p2 = Point(16, 26)
p2.x = 30
p2.moveBy(1, 1)
I came across this question with similar confusion, and after I answered it for myself it seemed prudent to report my findings here for prosperity.
As ThiefMaster already pointed out, the "owner" parameter makes possible constructions like a classproperty. Sometimes, you want classes to have methods masked as non-method attributes, and using the owner parameter allows you to do that with normal descriptors.
But that is only half the question. As for the "read-only" issue, here's what I found:
I first found the answer here: http://martyalchin.com/2007/nov/23/python-descriptors-part-1-of-2/. I did not understand it at first, and it took me about five minutes to wrap my head around it. What finally convinced me was coming up with an example.
Consider the most common descriptor: property. Let's use a trivial example class, with a property count, which is the number of times the variable count has been accessed.
class MyClass(object):
def __init__(self):
self._count = 0
#property
def count(self):
tmp = self._count
self._count += 1
return tmp
#count.setter
def setcount(self):
raise AttributeError('read-only attribute')
#count.deleter
def delcount(self):
raise AttributeError('read-only attribute')
As we've already established, the owner parameter of the __get__ function means that when you access the attribute at the class level, the __get__ function intercepts the getattr call. As it happens, the code for property simply returns the property itself when accessed at the class level, but it could do anything (like return some static value).
Now, imagine what would happen if __set__ and __del__ worked the same way. The __set__ and __del__ methods would intercept all setattr and delattr calls at the class level, in addition to the instance level.
As a consequence, this means that the "count" attribute of MyClass is effectively unmodifiable. If you're used to programming in static, compiled languages like Java this doesn't seem very interesting, since you can't modify classes in application code. But in Python, you can. Classes are considered objects, and you can dynamically assign any of their attributes. For example, let's say MyClass is part of a third-party module, and MyClass is almost entirely perfect for our application (let's assume there's other code in there besides the code for count) except that we wished the count method worked a little differently. Instead, we want it to always return 10, for every single instance. We could do the following:
>>> MyClass.count = 10
>>> myinstance = MyClass()
>>> myinstance.count
10
If __set__ intercepted the call to setattr(MyClass, 'count'), then there would be no way to actually change MyClass. Instead, the code for setcount would intercept it and couldn't do anything with it. The only solution would be to edit the source code for MyClass. (I'm not even sure you could overwrite it in a subclass, because I think defining it in a subclass would still invoke the setattr code. But I'm not sure, and since we're already dealing with a counterfactual here, I don't really have a way of testing it.)
Now, you may be saying, "That's exactly what I want! I intentionally did not want my user to reassign attributes of my class!" To that, all I can say is that what you wanted is impossible using naive descriptors, and I would direct you to the reasoning above. Allowing class attributes to be reassigned is much more in line with current Python idioms.
If you really, REALLY want to make a read-only class attribute, I don't think could tell you how. But if there is a solution, it would probably involve using metaclasses and either creating a property of the metaclass or modifying the metaclass's code for setattr and delattr. But this is Deep Magic, and well beyond the scope of this answer (and my own abilities with Python).
As far as read only properties are concerned (see discussion above), the following example shows how its done:
############################################################
#
# descriptors
#
############################################################
# define a class where methods are invoked through properties
class Point(object):
def getX(self):
print "getting x"
return self._x
def setX(self, value):
print "setting x"
self._x = value
def delX(self):
print "deleting x"
del self._x
x = property(getX, setX, delX)
p = Point()
p.x = 55 # calls setX
a = p.x # calls getX
del p.x # calls delX
# using property decorator (read only attributes)
class Foo(object):
def __init__(self, x0, y0):
self.__dict__["myX"] = x0
self.__dict__["myY"] = y0
#property
def x(self):
return self.myX
f = Foo(4,6)
print f.x
try:
f.x = 77 # fails: f.x is read-only
except Exception,e:
print e
The owner is just the class of the instance and is provided for convenience. You can always compute it from instance:
owner = instance.__class__
The __set__ method is supposed to change attributes on an instance. But what if you would like to change an attribute that is shared by all instances and therefore lives in the class, e.g., is a class attribute? This can only be done if you have access to the class, hence the owner argument.
Yes, you can overwrite the property / descriptor if you assign to an attribute through the class. This is by design, as Python is a dynamic language.
Hope that answers the question, although it was asked a long time ago.