On the way learning Python, I have read some class examples and tried to practice. But how can this piece of code can kill Spyder4.1.4 (Python3.8) by just running?
p=Point(101,202,0)
Kernel died, restarting...
class Point():
id=0
def __init__(self, x, y, z:float=0):
self._x = x; self._y = y; self._z = z; self.id+=1
#property
def x(self):
return self._x
#x.setter# to define a "non-public member"
def x(self, value):
self._x=value
#property
def y(self):
return self._y
#y.setter
def y(self, value):
self._y=value
#property
def xy(self):
return [self._x,self._y]
#xy.setter
def xy(self,xval,yval):
self._x=xval;self._y=yval
#property
def z(self):
return self._z
#z.setter
def z(self, value):
self._z=value
#property
def id(self):
return self.id
I have tried Jupyter notebook or https://ideone.com/hjUDTU. But none is working. I am really lost...
Related
I'm new to metaclasses, so I'm sorry, If my question is somehow stupid. I need to make a metaclass, that takes particular methods of a class and turns them into property methods or setters. So, if I have
class TestClass():
def __init__(self, x: int):
self._x = x
def get_x(self):
print("this is property")
return self._x
def set_x(self, x: int):
print("this is setter")
self._x = x
I want it to work like this:
class TestClass():
def __init__(self, x: int):
self._x = x
#property
def x(self):
print("this is property")
return self._x
#x.setter
def x(self, x: int):
print("this is setter")
self._x = x
For now I've just realized how to make it for property:
class PropertyConvert(type):
def __new__(cls, future_class_name, future_class_parents, future_class_attr):
new_attr = {}
for name, val in future_class_attr.items():
if not name.startswith('__'):
if name.startswith('get_'):
new_attr[name[4:]] = property(val)
if name.startswith('set_'):
# ???
else:
new_attr[name] = val
return type.__new__(cls, future_class_name, future_class_parents, new_attr)
But I can't figure out how to do it for setters.
I highly recommend docs about descriptors, they are really nice written with many similar examples explained.
But answering your question, to make a setter work you need to use the same property function but fill second arguments.
class property(fget=None, fset=None, fdel=None, doc=None)
So code could look like that:
class PropertyConvert(type):
def __new__(cls, future_class_name, future_class_parents, future_class_attr):
new_attr = {}
fget, fset, property_name = None, None, None
for name, val in future_class_attr.items():
if not name.startswith("__"):
if name.startswith("get_"):
property_name = name[4:]
fget = val
if name.startswith("set_"):
property_name = name[4:]
fset = val
else:
new_attr[name] = val
if n:
new_attr[property_name] = property(fget, fset)
return type.__new__(cls, future_class_name, future_class_parents, new_attr)
I have two class structured as below
from abc import ABCMeta, abstractmethod
class C(metaclass=ABCMeta):
""""""
def __init__(self, x, y):
self._x = x
self._y = y
#property
#abstractmethod
def x(self):
"""Get the _x"""
#x.setter
#abstractmethod
def x(self, value):
"""Set the x"""
#property
def y(self):
"""Get the _y"""
#y.setter
def y(self, value):
"""Set the _y"""
class D(C):
""""""
def __init__(self, x, y):
self._x = x
self._y = y
#property
def x(self):
return self._x
#C.x.setter
def x(self, value):
self._x = value
#property
def y(self):
return self._y
#C.y.setter
def y(self, value):
self._y = value
When I initialize an instance of D. It throws a error:
TypeError: Can't instantiate abstract class D with abstract methods x
When I rewrite setters decorator in D as
#x.setter
def x(self, value):
self._x = value
it works. But in python abc document https://docs.python.org/3/library/abc.html it states:
in disappreciated #abc.abstractproperty
If only some components are abstract, only those components need to be updated to create a concrete property in a subclass:
class D(C):
#C.x.setter
def x(self, val):
...
I don't know why write in this way will lead to error. Please help me understand the logic here. Thank you.
When you write #C.x.setter above your setter, you're setting x to a version of C.x with the setter replaced with your new setter function. Only the setter - the getter you wrote earlier is discarded. You're still using C.x's abstract getter.
The example in the docs uses #C.x.setter because they want the behavior it provides. In the doc example, C.x has a concrete getter, and they just want to replace the setter. That's not the case in your code.
How can I create multiple property decorators with self defined function as getter and setter based on following class structure? I have try to use
setattr(self, 'a', property(_to_get('a'), _to_set('a'))) but it does not work.
class ABC:
def __init__(self):
pass
def _to_get(self, attr):
return something_function(attr)
def _to_set(self, attr, value):
dosomething_function(attr, value)
#property
def a(self):
res = self._to_get('a')
return res.split(' ')[0]
#a.setter
def a(self, value)
self._to_set('a', value)
#property
def b(self):
res = self._to_get('b')
return res.split(' ')[1]
#b.setter
def b(self, value)
self._to_set('b', value)
#property
def c(self):
res = self._to_get('c')
return res.split(' ')[2]
#c.setter
def c(self, value)
self._to_set('c', value)
No reason why something like this wouldn't work:
class A(object):
def __init__(self):
self._a = None
#property
def a(self):
return self._a
#a.setter
def a(self, x):
self._a = x
#a.deleter
def a(self):
del self._a
#property
def b(self):
return self._b
#b.setter
def b(self, x):
self._b = x
#b.deleter
def b(self):
del self._b
#property
def c(self):
return self._c
#c.setter
def c(self, x):
self._c = x
#c.deleter
def c(self):
del self._c
Consider your original class written without decorator syntax. (The translation may not be 100% accurate, but should be close enough to illustrate the point I want to make.)
class ABC:
def _to_get(self, attr):
return something_function(attr)
def _to_set(self, attr, value):
dosomething_function(attr, value)
a = property(lambda self: ABC._to_get(self, 'a').split(' ')[0],
lambda self, value: ABC._to_set(self, 'a', value))
b = property(lambda self: ABC._to_get(self, 'b').split(' ')[1],
lambda self, value: ABC._to_set(self, 'b', value))
c = property(lambda self: ABC._to_get(self, 'c').split(' ')[2],
lambda self, value: ABC._to_set(self, 'c', value))
a, b and c are all basically the same thing, but parameterized
by the name of the property and an integer.
def make_getter(attr, x):
def getter(self):
return self._to_get(attr).split(' ')[x]
return getter
def make_setter(attr):
def setter(self, value):
self._to_set(attr, value)
return setter
class ABC:
def _to_get(self, attr):
return something_function(attr)
def _to_set(self, attr, value):
dosomething_function(attr, value)
a = property(make_getter('a', 0), make_setter('a'))
b = property(make_getter('b', 1), make_setter('b'))
c = property(make_getter('c', 2), make_setter('c'))
Something like the following should also work (not heavily tested), moving the logic into a subclass of property.
class Foo(property):
def __init__(self, x):
super().__init__(self._to_get, self._to_set)
self.x = x
# name is the class attribute the instance of Foo
# will be assigned to
def __set_name__(self, owner, name):
self.attr = name
# In both of the following, obj is the instance that actually
# invokes the parameter. You would probably want to pass it
# to something_function and do_something as well.
def _to_get(self, obj):
return something_function(self.attr).split(' ')[self.x]
def _to_set(self, obj, value):
do_something(self.attr, value)
class ABC:
a = Foo(0) # Will call a.__set_name__(ABC, 'a')
b = Foo(1) # Will call b.__set_name__(ABC, 'b')
c = Foo(2) # Will call c.__set_name__(ABC, 'c')
In the following code snippet, I use a setter on the x attribute (which I'd like to keep private)
class test:
def __init__(self, pos, x):
self._pos = pos
self.x = x # Want to be a private variable, eg. self._x
#property
def x(self):
return self._x
#x.setter
def x(self, x):
# Logic for setting x
if self._pos == 'long':
self._x = -1 * abs(x)
elif self._pos == 'short':
self._x = abs(x)
else:
raise ValueError('$$ pos must be long or short')
The problem is I end up with TWO attributes , self.x AND self._x . Since I want x to be private - I'd like to only have self._x (and discard self.x) . What's missing in the code ?
I used python 2.5, I want to know how can change the next code when the Platform is python2.5 or python2.6
class C(object):
def __init__(self):
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
a=C()
print a.x#error
thanks
thanks ,alex,i think property must be 3 arguments in your example
but ,i have seen a code which with 'property' only use 1 argumennt ,why,can it work
class SortingMiddleware(object):
def process_request(self, request):
request.__class__.field = property(get_field)
request.__class__.direction = property(get_direction)
Python 2.5 does not support the .setter and .deleter sub-decorators of property; they were introduced in Python 2.6.
To work on both releases, you can, instead, code something like:
class C(object):
def __init__(self):
self._x = None
def _get_x(self):
"""I'm the 'x' property."""
return self._x
def _set_x(self, value):
self._x = value
def _del_x(self):
del self._x
x = property(_get_x, _set_x, _del_x)