Suppose I have a class:
import copy
class TestClass:
def __init__(self, value):
if isinstance(value, TestClass):
self = value.deepcopy()
else:
self.data = value
def deepcopy(self):
return copy.deepcopy(self)
where I want to write the code such that if an instance of a class is initialized by another instance of the same class, it will make a deepcopy of the second class.
Now, if I try
In []: x = TestClass(3)
In []: x.data
Out[]: 3
But if I try
In []: y = TestClass(x)
Out[]: y.data
...
AttributeError: 'TestClass' object has no attribute 'data'
Why didn't the deepcopy happen when the instance x was passed to y ?
A solution:
import copy
class TestClass:
def __init__(self, value):
if isinstance(value, TestClass):
self.__dict__ = copy.deepcopy(value.__dict__)
else:
self.data = value
This makes your example working. You want to do a 'copy constructor',
in Python objects have a __dict__ attribute containing all
members so you can just copy the dictionary from the original object
and assign it to the new object __dict__.
Related
The script below tries to remove the Inner1 nested class from the class Outer. I get the clear error "TypeError: can't delete __class__ attribute". Dead end?
def Loader(cls):
for key in dir(cls):
value = getattr(cls, key)
if isinstance(value, type):
delattr(cls, key)
return cls
#Loader
class Outer:
class Inner1:
X = 1
print(Outer.Inner1.X)
The problem is that the following line:
if isinstance(value, type):
Is matching everything that inherits from type. If you wish to only delete that inner classes that are explicitly defined in the decorated class, you could use something like this:
from inspect import isclass
def Loader(cls):
# vars returns the __dict__ attributes, so only names that are explicitely defined in cls.
for key, value in vars(cls).copy().items():
# If it is a class, delete it
if isclass(value):
delattr(cls, key)
return cls
#Loader
class Outer:
class Inner1:
X = 1
print(Outer.Inner1.X) # AttributeError: type object 'Outer' has no attribute 'Inner1'
An alternative, would be to make your decorator a class and tell it specifically the names you want to delete. E.g.
class Loader:
def __init__(self, *args):
# Store the given names to delete
self.names = args
def __call__(self, cls):
for name in self.names:
try:
delattr(cls, name)
except AttributeError:
pass
return cls
#Loader('Inner1', 'Inner2', 'etc')
class Outer:
class Inner1:
X = 1
print(Outer.Inner1.X) # AttributeError: type object 'Outer' has no attribute 'Inner1'
That said, I'm not sure why you would decorate a class and define inner classes that you will dynamically delete... why not... just not define them at all? :D
I would like to write a custom list class in Python 3 like in this question How would I create a custom list class in python?, but unlike that question I would like to implement __get__ and __set__ methods. Although my class is similar to the list, but there are some magic operations hidden behind these methods. And so I would like to work with this variable like with list, like in main of my program (see below). I would like to know, how to move __get__ and __set__ methods (fget and fset respectively) from Foo class to MyList class to have only one class.
My current solution (also, I added output for each operation for clarity):
class MyList:
def __init__(self, data=[]):
print('MyList.__init__')
self._mylist = data
def __getitem__(self, key):
print('MyList.__getitem__')
return self._mylist[key]
def __setitem__(self, key, item):
print('MyList.__setitem__')
self._mylist[key] = item
def __str__(self):
print('MyList.__str__')
return str(self._mylist)
class Foo:
def __init__(self, mylist=[]):
self._mylist = MyList(mylist)
def fget(self):
print('Foo.fget')
return self._mylist
def fset(self, data):
print('Foo.fset')
self._mylist = MyList(data)
mylist = property(fget, fset, None, 'MyList property')
if __name__ == '__main__':
foo = Foo([1, 2, 3])
# >>> MyList.__init__
print(foo.mylist)
# >>> Foo.fget
# >>> MyList.__str__
# >>> [1, 2, 3]
foo.mylist = [1, 2, 3, 4]
# >>> Foo.fset
# >>> MyList.__init__
print(foo.mylist)
# >>> Foo.fget
# >>> MyList.__str__
# >>> [1, 2, 3, 4]
foo.mylist[0] = 0
# >>> Foo.fget
# >>> MyList.__setitem__
print(foo.mylist[0])
# >>> Foo.fget
# >>> MyList.__getitem__
# >>> 0
Thank you in advance for any help.
How to move __get__ and __set__ methods (fget and fset respectively) from Foo class to MyList class to have only one class?
UPD:
Thanks a lot to #Blckknght! I tried to understand his answer and it works very well for me! It's exactly what I needed. As a result, I get the following code:
class MyList:
def __init__(self, value=None):
self.name = None
if value is None:
self.value = []
else:
self.value = value
def __set_name__(self, owner, name):
self.name = "_" + name
def __get__(self, instance, owner):
return getattr(instance, self.name)
def __set__(self, instance, value):
setattr(instance, self.name, MyList(value))
def __getitem__(self, key):
return self.value[key]
def __setitem__(self, key, value):
self.value[key] = value
def append(self, value):
self.value.append(value)
def __str__(self):
return str(self.value)
class Foo:
my_list = MyList()
def __init__(self):
self.my_list = [1, 2, 3]
print(type(self.my_list)) # <class '__main__.MyList'>
self.my_list = [4, 5, 6, 7, 8]
print(type(self.my_list)) # <class '__main__.MyList'>
self.my_list[0] = 10
print(type(self.my_list)) # <class '__main__.MyList'>
self.my_list.append(7)
print(type(self.my_list)) # <class '__main__.MyList'>
print(self.my_list) # [10, 5, 6, 7, 8, 7]
foo = Foo()
I don't know, that's Pythonic way or not, but it works as I expected.
In a comment, you explained what you actually want:
x = MyList([1])
x = [2]
# and have x be a MyList after that.
That is not possible. In Python, plain assignment to a bare name (e.g., x = ..., in contrast to x.blah = ... or x[0] = ...) is an operation on the name only, not the value, so there is no way for any object to hook into the name-binding process. An assignment like x = [2] works the same way no matter what the value of x is (and indeed works the same way regardless of whether x already has a value or whether this is the first value being assigned to x).
While you can make your MyList class follow the descriptor protocol (which is what the __get__ and __set__ methods are for), you probably don't want to. That's because, to be useful, a descriptor must be placed as an attribute of a class, not as an attribute of an instance. The properties in your Foo class creating separate instances of MyList for each instance. That wouldn't work if the list was defined on the Foo class directly.
That's not to say that custom descriptors can't be useful. The property you're using in your Foo class is a descriptor. If you wanted to, you could write your own MyListAttr descriptor that does the same thing.
class MyListAttr(object):
def __init__(self):
self.name = None
def __set_name__(self, owner, name): # this is used in Pyton 3.6+
self.name = "_" + name
def find_name(self, cls): # this is used on earlier versions that don't support set_name
for name in dir(cls):
if getattr(cls, name) is self:
self.name = "_" + name
return
raise TypeError()
def __get__(self, obj, owner):
if obj is None:
return self
if self.name is None:
self.find_name(owner)
return getattr(obj, self.name)
def __set__(self, obj, value):
if self.name is None:
self.find_name(type(obj))
setattr(obj, self.name, MyList(value))
class Foo(object):
mylist = MyListAttr() # create the descriptor as a class variable
def __init__(self, data=None):
if data is None:
data = []
self.mylist = data # this invokes the __set__ method of the descriptor!
The MyListAttr class is more complicated than it otherwise might be because I try to have the descriptor object find its own name. That's not easy to figure out in older versions of Python. Starting with Python 3.6, it's much easier (because the __set_name__ method will be called on the descriptor when it is assigned as a class variable). A lot of the code in the class could be removed if you only needed to support Python 3.6 and later (you wouldn't need find_name or any of the code that calls it in __get__ and __set__).
It might not seem worth writing a long descriptor class like MyListAttr to do what you were able to do with less code using a property. That's probably correct if you only have one place you want to use the descriptor. But if you may have many classes (or many attributes within a single class) where you want the same special behavior, you will benefit from packing the behavior into a descriptor rather than writing a lot of very similar property getter and setter methods.
You might not have noticed, but I also made a change to the Foo class that is not directly related to the descriptor use. The change is to the default value for data. Using a mutable object like a list as a default argument is usually a very bad idea, as that same object will be shared by all calls to the function without an argument (so all Foo instances not initialized with data would share the same list). It's better to use a sentinel value (like None) and replace the sentinel with what you really want (a new empty list in this case). You probably should fix this issue in your MyList.__init__ method too.
Say I have a class called A and i want to list all the objects created from that particular class. This is what i have done till now and It raises AttributeError: type object 'A' has no attribute 'items' How to do this?
class A:
def __init__(self):
self.items = []
self.items.append(self)
#classmethod
def list_objects(cls):
return cls.items
a = A()
b = A()
print(A.list_objects())
# Expected output is [a,b]
You would need the list to be at the class level, not instance level
class A:
items = []
def __init__(self):
A.items.append(self)
#classmethod
def list_objects(cls):
return cls.items
Then you would see
>>> a = A()
>>> b = A()
>>> A.list_objects()
[<__main__.A object at 0x02B77230>, <__main__.A object at 0x02B772D0>]
The problem with your code is in the self.items = [] part, as you initialise a new items empty list for each instance of class A you create. So in your case each object of class A will have an instance member items, containing itself only.
So first of all you need to move your items list to the class level, and then in the __init__ add self to that list.
If you are going to need this functionality for many classes, I would suggest to go with the following solution:
#track_objects
class A:
def __init__(self):
pass # your init code here
>>> a = A()
>>> b = A()
>>> A.items
[<__main__.A instance at 0x1004873f8>, <__main__.A instance at 0x100487488>]
and this is the #track_objects implementation:
def track_objects(klass):
klass.items = []
orig_init = klass.__init__
def init_wrapper(self, *args, **kwargs):
self.items.append(self)
return orig_init(self, *args, **kwargs)
klass.__init__ = init_wrapper
return klass
I am using a code snippet from here along with my own modifications in ironpython which works extremly well:
from System.ComponentModel import INotifyPropertyChanged, PropertyChangedEventArgs
from Library.pyevent import make_event
class Notify_property(property):
''' defines a notifiable property
'''
def __init__(self, getter):
def newgetter(slf):
#return None when the property does not exist yet
try:
return getter(slf)
except AttributeError:
return None
super(Notify_property, self).__init__(newgetter)
def setter(self, setter):
def newsetter(slf, newvalue):
# do not change value if the new value is the same
# trigger PropertyChanged event when value changes
oldvalue = self.fget(slf)
if oldvalue != newvalue:
setter(slf, newvalue)
slf.OnPropertyChanged(setter.__name__)
return property(
fget=self.fget,
fset=newsetter,
fdel=self.fdel,
doc=self.__doc__)
class NotifyPropertyChangedBase(INotifyPropertyChanged):
''' The base of the MVVM view model
Here the bound properties are added in addition with its
handlers.
'''
# handlers which get fired on any change register here
PropertyChanged = None
''' handlers that only get fired on their property change register here
they are organized in a dictionary with the property name as key and
a list of handlers as value
'''
_property_handlers = {}
def __init__(self):
''' we create an event for the property changed event
'''
self.PropertyChanged, self._propertyChangedCaller = make_event()
def add_PropertyChanged(self, value):
''' helper function to wrap the += behaviour
'''
self.PropertyChanged += value
def remove_PropertyChanged(self, value):
''' helper function to wrap the -= behaviour
'''
self.PropertyChanged -= value
def OnPropertyChanged(self, propertyName):
''' gets fired on an property changed event
'''
if self.PropertyChanged is not None:
self._propertyChangedCaller(self, PropertyChangedEventArgs(propertyName))
try:
for property_handler in self._property_handlers[propertyName]:
property_handler(propertyName,PropertyChangedEventArgs(propertyName))
except KeyError:
pass
def add_notifiable_property(self, notifiable_property):
self.add_handled_property((notifiable_property,None))
def add_notifiable_property_list(self, *symbols):
for symbol in symbols:
self.add_notifiable_property(symbol)
def add_handled_property_list(self, *symbols):
for symbol in symbols:
self.add_handled_property(symbol)
def add_handled_property(self, notifiable_property):
symbol = notifiable_property[0]
if notifiable_property[1] is not None:
self._property_handlers[notifiable_property[0]] = notifiable_property[1]
dnp = """
import sys
sys.path.append(__file__)
#Notify_property
def {0}(self):
return self._{0}
#{0}.setter
def {0}(self, value):
self._{0} = value
""".format(symbol)
d = globals()
exec dnp.strip() in d
setattr(self.__class__, symbol, d[symbol])
Now I must admit that I not fully understand all of the code. Mainly the use of the Notify_property class is an enigma to me. To get a better understanding of the code I tried to remove a property. Calling from my MainViewModel which subclasses the above class I can define a property via:
add_notifiable_property('TestProperty')
or
add_handled_property((TestProperty,[handler1,handler2])
I can also delete handlers (not yet implemeted) but how to I remove a property again?
del self.TestProperty
excepts with
undeletable attribute
and
delattr(self,'TestProperty')
excepts with
delattr takes exactly 2 arguments 2 given
hmm very strange.
I also tried to add a function to my base class:
def remove_notifiable_property(self,propertyname):
''' removes a notifiable property
'''
self._property_handlers.pop(propertyname,None)
exec "del self.{0}".format(propertyname)
but get the same error about an undeletable attribute.
How can I remove a set property again?
EDIT: I found out I was missing the deleter function. Adding this code to the above dnp string now leads to a new error:
#{0}.deleter
def {0}(self):
del self._{0}
with new error:
Derived calss has no attribute _TestProperty
with TestProperty being the name I added. Still stuck.
EDIT2:
I tracked it down to the following:
class C(object):
def __init__(self):
pass#self._x = None
#property
def x(self):
"""I'm the 'x' property."""
return self._x
#x.setter
def x(self, value):
self._x = value
#x.deleter
def x(self):
del self._x
c = C()
print dir(c)
c.x = 'A'
print c.x
print dir(c)
del c.x
print dir (c)
shows the same behavior. The error no _ came from missing initializing the attribute. Adding an:
exec """self._{0} = None""".format(symbol)
to the last line of the add_handled_property fixes it.
But still the attribute itself is shown with dir, also it is not in the class anymore. Is this a bug in python?
You should be able to do
delattr(self.__class__, 'TestProperty')
because the properties are in the class's __dict__. See the last line:
setattr(self.__class__, symbol, d[symbol])
Example of how properties work in Python using the class A, instance a and property p in the class A:
>>> class A(object):
class Property(object):
def __get__(*args):
print 'get:', args
def __set__(*args):
print 'set:', args
def __delete__(*args):
print 'del:', args
p = Property()
>>> A.p
get: (<__main__.Property object at 0x7f3e16da4690>, None, <class '__main__.A'>)
>>> a = A()
>>> a.p
get: (<__main__.Property object at 0x7f3e16da4690>, <__main__.A object at 0x7f3e16da4910>, <class '__main__.A'>)
>>> a.p = 3
set: (<__main__.Property object at 0x7f3e16da4690>, <__main__.A object at 0x7f3e16da4910>, 3)
>>> del a.p
del: (<__main__.Property object at 0x7f3e16da4690>, <__main__.A object at 0x7f3e16da4910>)
you can replace them in the class
>>> A.p = 2
>>> a.p
2
or delete them from the class
>>> A.p = A.Property()
>>> del A.p
I have something like this:
class SomeObject:
#code to access parents MyVar
class MyClass:
MyVar = 3
MyObject = SomeObject()
I need to access MyVar from inside MyObject. Is there any way I can do that?
Thank you!
You can store a reference to the MyClass object in the SomeObject. You can initialise the reference when you make an constructor with a MyClass Object as parameter.
class SomeObject:
def __init__(self, reference):
self.reference_=reference
#code to access parents MyVar
self.reference_.MyVar=5
class MyClass:
MyVar = 3
MyObject = SomeObject(self)
As unutbu stated my code was not running, therefore a more detailed example.
class SomeObject:
def __init__(self):
self.reference_=None
def connect(self, reference):
self.reference_=reference
#code to access parents MyVar
def call(self):
self.reference_.MyVar=5
class MyClass:
MyVar = 3
MyObject = SomeObject()
def connect(self):
self.MyObject.connect(self)
if __name__ == '__main__':
myclass = MyClass()
myclass.connect()
myclass.MyObject.call()
print(myclass.MyVar)
You have to store a reference to your parent, but you can make that magic happen automatically:
from weakref import ref
class MyClass(object):
def __setattr__(self, key, value):
self.__dict__[key] = value
try:
value._parent = ref(self)
except AttributeError:
raise TypeError('MyClass cannot have children of type ' +
type(value).__name__)
def __delattr__(self, key):
v = self.__dict__[key]
del self.__dict__[key]
try:
v._parent = None
except AttributeError:
raise TypeError('Child of MyClass is mysteriously '
'missing its parent')
class SomeObject(object):
_parent = None
#property
def parent(self):
if self._parent is not None:
return self._parent()
return None
>>> a = MyClass()
>>> a.b = SomeObject()
>>> print a.b.parent
<__main__.MyClass at 0x8ce60f0>
>>> b = a.b
>>> del a.b
>>> print b.parent
None
By overriding the __setattr__ and __delattr__ operators you can control the child's view of its parent and make sure that the connection is always correct. Furthermore, this avoids using clumsy add/remove methods; methods you may accidentally forget to use. This restricts your objects to having exactly one parent, but for these types of models, that is generally desirable.
Lastly, I recommend that rather than holding a reference to the parent object directly, you hold a weak reference. This avoids cyclic references that may confuse the garbage collector (a holds a reference to b, which holds a reference to a. Their reference count never goes to 0, so they aren't garbage collected).