It happens that I have two property implementations. but one works and the other does not. That is, that in one it does not even enter the setters.
I gave myself the task of wandering online, and this happens to me is very rare, both are supposed to work.
Do you know why this happens?
Thank you
class QuickTasks():
def __init__(self, name=None, value=None):
self.name = name
self.value = value
#property
def name(self):
return self._name
#name.setter
def name(self, value):
if isinstance(value, str):
self._name = value
else:
raise TypeError("name must be str")
#property
def value(self):
return self._value
#value.setter
def value(self, value):
if isinstance(value, int):
self._value = value
else:
raise TypeError("value must be int")
obj = QuickTasks(name=4, value='j')
print(obj.name)
obj.name = 5
print(obj.name)
################################################################
class TreeNode(object):
def __init__(self, value = None):
self.value = value
self._left_node = None
self._right_node = None
#property
def value(self):
return self._value
#value.setter
def value(self, value):
if isinstance(value, int):
self._value = 8
else:
raise TypeError("value must be int")
def main():
tree_node = TreeNode(3)
#tree_node.value = 3
print (tree_node.value)
if __name__ == '__main__':
print("")
main()
When you hit the line:
obj = QuickTasks(name=4, value='j')
and it assigns to self.name in the QuickTasks initializer, it raises a TypeError, which you don't catch. That bypasses the rest of your code entirely, skipping all uses of the value property (and of TreeNode entirely).
Related
I have a class like this:
class A:
b = ""
#property
some_property(self):
raise Error()
#some_property.setter
def some_property(self, some_property_var)
self.b = some_property_var
and i want to do A(some_property='A'), but i can't because
TypeError: A() takes no arguments
. Is there any way to make it work?
I think this is a better example of how to demonstrate what you are trying to do;
#set a property in a class in python
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
#property
def name(self):
return self._name
#name.setter
def name(self, name):
self._name = name
#property
def age(self):
return self._age
#age.setter
def age(self, age):
self._age = age
p = Person("John", 36)
So in your case;
class A:
def __init__(self, b):
self.b = b
#property
def b(self):
return self._b
#b.setter
def b(self, b):
self._b = b
a = A(1)
print(a.b)
Based on your comments;
#create a class that takes keyword arguments
class A:
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
a = A(a=1, b=2)
Let's consider the following example:
class SubClass:
def __init__(self):
self._param = None
#property
def param(self):
print('read param')
return self._param
#param.setter
def param(self, value):
print('set param')
self._param = value
class MainClass:
def __init__(self):
self._var = SubClass()
#property
def var(self):
print('read var')
return self._var
#var.setter
def var(self, value):
print('set var')
self._var = value
If I do:
cls = MainClass()
cls.var.param = 3
I obtain:
'read var'
'set param'
How can I make MainClass aware that var.param has changed?
Useful additional info: consider that in my actual code param is not a scalar but an array with hundreds of elements, so I would like to avoid to create a copy and then just compare them. Moreover, param and var are not the only properties.
One approach is to pass a method from MainClass as an argument when instantiating SubClass, and have SubClass call it whenever it changes a variable:
class SubClass:
def __init__(self, changed):
self._param = None
self.changed = changed
#property
def param(self):
print('read param')
return self._param
#param.setter
def param(self, value):
print('set param')
self._param = value
self.changed('param')
class MainClass:
def __init__(self):
self._var = SubClass(self.changed)
def changed(self, name):
print("Subclass changed: ", name, getattr(self._var, name))
#property
def var(self):
print('read var')
return self._var
#var.setter
def var(self, value):
print('set var')
self._var = value
m = MainClass()
m.var.param = 'test'
class data_attr_set_pass(object):
def __init__(self, inner=None, name=""):
self.inner = inner
self.name = name
def __get__(self, instance, owner):
return self.inner
def __set__(self, instance, value):
pass
def __repr__(self):
return str(self.name) + ":" + str(self.inner)
class data_attr(object):
def __init__(self, inner=None, name=""):
self.inner = inner
self.name = name
def __get__(self, instance, owner):
return self.inner
def __set__(self, instance, value):
self.inner = value
def __repr__(self):
return str(self.name) + ":" + str(self.inner)
class non_data_attr(object):
def __init__(self, inner=None, name=""):
self.inner = inner
self.name = name
def __get__(self, instance, owner):
return self.inner
def __repr__(self):
return str(self.name) + ":" + str(self.inner)
class Myclass(object):
x = data_attr_set_pass(11, "class attr")
def __init__(self):
self.x = data_attr(890, "instance attr")
print(Myclass.x)
m = Myclass()
print(Myclass.x)
print(m.x)
print(Myclass.__dict__)
print(m.__dict__)
output:
11
11
11
{'__module__': '__main__', 'x': class attr:11, '__init__': <function Myclass.__init__ at 0x7f0dcc2d1378>, '__dict__': <attribute '__dict__' of 'Myclass' objects>, '__weakref__': <attribute '__weakref__' of 'Myclass' objects>, '__doc__': None}
{}
I had some tests for usage of python data-descriptor. And one test had very strange behaviour. I know that descriptor should be class attr, it's just a test:).
I put pass in __set__ of class data_attr_set_pass. So as far as I know, self.x = data_attr(890, "instance attr") in __init__ will put data_attr(890, "instance attr") into self.__dict__. But it can be found nowhere.
why does it happend and where can I find the data-descriptor?
==================================================================
It's really my misunderstanding, I just run first test in code below in which I put 2 into self.__dict__["x"] directly, the second one can prove it.
class Myclass(object):
x = data_attr_set_pass(11, "class attr")
def __init__(self):
self.__dict__["x"] = 2
print(Myclass.x)
m = Myclass()
print(Myclass.x)
print(m.x)
print(Myclass.__dict__)
print(m.__dict__)
print('================')
class Myclass(object):
x = data_attr_set_pass(1324, "class attr")
def __init__(self):
self.x = 2232
print(Myclass.x)
m = Myclass()
print(Myclass.x)
print(m.x)
print(Myclass.__dict__)
print(m.__dict__)
You say
I put pass in __set__ of class data_attr_set_pass. So as far as I know, self.x = data_attr(890, "instance attr") in __init__ will put data_attr(890, "instance attr") into self.__dict__.
But why would it do that? You didn't write data_attr_set_pass.__set__ to insert the value into the instance __dict__. You wrote your __set__ to do nothing but pass.
Assigning something to self.x does exactly what you wrote your __set__ to do: nothing.
I am trying to work on some problem where I could use this code to learn how to implement Binary Search Tree, this code is based on http://interactivepython.org/runestone/static/pythonds/Trees/SearchTreeImplementation.html however there are come bugs in the original code, which I think should be fixed by doing the following.
Edited this code to remove a syntax error.
class TreeNode(object):
def __new__(cls, *args, **kwargs):
return object.__new__(cls, args, kwargs)
def __init__(self, key, val, left=None, right=None, parent=None):
self.key = key
self.payload = val
self.leftChild = left
self.rightChild = right
self.parent = parent
def hasLeftChild(self):
return self.leftChild
def hasRightChild(self):
return self.rightChild
def isLeftChild(self):
return self.parent and self.parent.leftChild == self
def isRightChild(self):
return self.parent and self.parent.rightChild == self
def isRoot(self):
return not self.parent
def isLeaf(self):
return not(self.leftChild or self.rightChild)
def hasAnyChildren(self):
return self.leftChild or self.rightChild
def hasBothChildren(self):
return self.leftChild and self.rightChild
def replaceNodeData(self, key, value, lc, rc):
self.key = key
self.payload = value
self.leftChild = lc
self.rightChild = rc
if self.hasLeftChild():
self.leftChild.parent = self
if self.hasRightChild():
self.rightChild.parent = self
class BinarySearchTree(TreeNode):
def __init__(self):
# self.root is refrence to TreeNode, which is root
# of primary TreeNode
self.root = None
self.size = 0
def size(self):
return self.size
def __len__(self):
return self.size
def __iter__(self):
return self.root.__iter__()
def put(self, key, val):
# checks if there is root
# if there is a root, lets transverse along tree
if self.root:
self._put(key, val, self.root)
else:
# else if there is not root, lets set a root
# which is a TreeNode instance
self.root = super(BinarySearchTree, cls).__new__(key, val)
self.size = self.size + 1
def _put(self, key, val, currentNode):
if key < currentNode.key:
if currentNode.hasLeftChild():
self._put(key, val, currentNode.leftChild)
else:
currentNode.leftChild = super(BinarySearchTree, cls).__new__(
key, val, parent=currentNode)
else:
if currentNode.hasRightChild():
self._put(key, val, currentNode.rightChild)
else:
currentNode.rightChild = super(BinarySearchTree, cls).__new__(
key, val, parent=currentNode)
def __setitem__(self, k, v):
self.put(k, v)
def get(self, key):
if self.root:
res = self._get(key, self.root)
if res:
return res.payload
else:
return None
else:
return None
def _get(self, key, currentNode):
if not currentNode:
return None
if key == currentNode.key:
return currentNode
if key < currentNode.key:
return self._get(key, currentNode.leftChild)
else:
return self._get(key, currentNode.rightChild)
def __getitem__(self, key):
# this method allows to access instance[index]
# format.
return self.get(key)
def __contains__(self, key):
"""
__contains__ overloads the 'in' operator
allows us to do something simliar to
if 'Northfield' in myZipTree:
print("oom ya ya")
"""
if self._get(key, self.root):
return True
else:
return False
def delete(self, key):
if self.size > 1:
nodeToRemove = self._get(key, self.root)
if nodeToRemove:
self.remove(nodeToRemove)
self.size = self.size - 1
else:
raise KeyError('Error, key was not found')
elif self.size == 1 and self.root.key == key:
self.root = None
self.size = self.size - 1
else:
raise KeyError('Error, key was not found')
def __delitem__(self, key):
self.delete(key)
def remove(self, currentNode):
if currentNode.isLeaf():
# current Leaf has only one child
if currentNode == currentNode.parent.leftChild:
currentNode.parent.leftChild = None
else:
currentNode.parent.rightChild = None
elif currentNode.hasBothChildren():
# interior
succ = currentNode.findSuccessor()
succ.spliceOut()
currentNode.key = succ.key
currentNode.payload = succ.payload
else:
# The node to be deleted has only one child.
if currentNode.hasLeftChild():
if currentNode.isLeftChild():
currentNode.leftChild.parent = currentNode.parent
currentNode.parent.leftChild = currentNode.leftChild
elif currentNode.isRightChild():
currentNode.rightChild.parent = currentNode.parent
currentNode.parent.rightChild = currentNode.rightChild
else:
# this is a root node, replace it's data with it's
# left children
self.replaceNodeData(
currentNode.leftChild.key,
currentNode.leftChild.value,
currentNode.leftChild,
currentNode.rightChild
)
else:
if currentNode.isLeftChild():
currentNode.leftChild.parent = currentNode.parent
currentNode.parent.leftChild = currentNode.leftChild
elif currentNode.isRightChild():
currentNode.rightChild.parent = currentNode.parent
currentNode.parent.rightChild = currentNode.rightChild
else:
# this is a root node, replace it's data with it's
# left children
self.replaceNodeData(
currentNode.leftChild.key,
currentNode.leftChild.value,
currentNode.leftChild,
currentNode.rightChild
)
def __iter__(self):
if self:
if self.hasLeftChild():
for elem in self.leftChiLd:
yield elem
yield self.key
if self.hasRightChild():
for elem in self.rightChild:
yield elem
def findSuccessor(self):
succ = None
if self.hasRightChild():
succ = self.rightChild.findMin()
else:
if self.parent:
if self.hasLeftChild():
succ = self.parent
else:
self.parent.rightChild = None
succ = self.parent.findSuccessor()
self.parent.rightChild = self
return self
def findMin(self):
if self.hasLeftChild():
return self.leftChild.findMin()
else:
return self
def spliceOut(self):
if self.isLeaf():
if self.isLeftChild():
self.parent.leftChild = None
else:
self.parent.rightChild = None
elif self.hasAnyChildren():
if self.hasLeftChild():
if self.isLeftChild():
self.parent.leftChild = self.leftChild
else:
self.parent.rightChild = self.leftChild
self.leftChild.parent = self.parent
else:
if self.isLeftChild():
self.parent.leftChild = self.rightChild
else:
self.parent.rightChild = self.rightChild
self.rightChild.parent = self.parent
mytree = BinarySearchTree()
mytree[17] = "17"
mytree[5] = "5"
mytree[35] = "35"
mytree[2] = "2"
mytree[11] = "11"
mytree[9] = "9"
mytree[16] = "16"
mytree[7] = "7"
mytree[29] = "29"
mytree[38] = "38"
del(mytree[5])
print(mytree[5])
print(mytree[7])
print(mytree.size)
however when I running the code I am getting the following error, I am not very good in OOPs concepts, any educationatory lessons will be appreciated.
# vaibhavchauhan # startup001 in ~/Documents/Projects/interactivepython [19:17:01] C:1
$ python3 binarySearchTreeMod.py
Traceback (most recent call last):
File "binarySearchTreeMod.py", line 248, in <module>
mytree = BinarySearchTree()
File "binarySearchTreeMod.py", line 3, in __new__
return object.__new__(cls, args, kwargs)
TypeError: object() takes no parameters
That __new__ method shouldn't be there. Just remove it. It has a particular purpose, but if you're just learning you shouldn't need it at all.
The __new__ method is primarily used for subclassing immutable objects. That's not what you have, so you don't need it.
See the documentation for more information.
What is the simplest / most pythonic way to override only the setter of an abstract property in Python 3? Variant 3 seems to mean the least effort for the derived class implementor. Is it correct? Does it have disadvantages?
import abc
class A1(metaclass=abc.ABCMeta):
def __init__(self, x, **kwargs):
super().__init__(**kwargs)
self._x = x
#property
def x(self):
return self._x
#x.setter
#abc.abstractmethod
def x(self, value):
self._x = value
class B1(A1):
#property
def x(self):
return super().x
#x.setter
def x(self, value):
print("B1 setter")
super(B1, self.__class__).x.fset(self, value)
b1 = B1(x=1)
b1.x = 3
print(b1.x)
class A2(metaclass=abc.ABCMeta):
def __init__(self, x, **kwargs):
super().__init__(**kwargs)
self._x = x
#abc.abstractmethod
def _get_x(self):
return self._x
#abc.abstractmethod
def _set_x(self, value):
self._x = value
x = property(_get_x, _set_x)
class B2(A2):
def _get_x(self):
return super()._get_x()
def _set_x(self, value):
print("B2 setter")
super()._set_x(value)
x = property(_get_x, _set_x)
b2 = B2(x=1)
b2.x = 3
print(b2.x)
class A3(metaclass=abc.ABCMeta):
def __init__(self, x, **kwargs):
super().__init__(**kwargs)
self._x = x
def _get_x(self):
return self._x
#abc.abstractmethod
def _set_x(self, value):
self._x = value
x = property(
lambda self: self._get_x(),
lambda self, value: self._set_x(value))
class B3(A3):
def _set_x(self, value):
print("B3 setter")
super()._set_x(value)
b3 = B3(x=1)
b3.x = 3
print(b3.x)
So, yes, you listed a lot of ways in there - and although the one that requires more code is your variant 3, the most straighforard, least surprising way to do it is your variant 1 -
It just works, and is perfectly readable, no surprises - and there seems to be no simpler way than calling fget explicitly there.