Reputation: 347
And by best, I mean both Pythonically and with as few lines of code as possible.
I am used to defining classes with attributes in Python like so.
class MyClass(object):
def __init__(self):
self.value = 5
Often, however, I find myself needing to ensure that self.value
can only take a specific type of value. In the past (coming from a PHP OOP background) I've done this with a getter-setter method similar to this:
def value(self, new_value = -1)
if new_value == -1:
return self.value
if isinstance(new_value, int) and new_value > -1:
self.value = new_value
else:
raise TypeError
I then access self.value
with self.value()
, and set it with self.value(67)
. Usually I'll name-mangle self.value
to self.__value
to discourage its direct access.
I've recently discovered (or am in the process of discovering) descriptors. Following closely the excellent discussion here I've written something that looks like this:
class ValidatedAttribute(object):
def __init__(self, key, kind):
self.key = key
self.kind = kind
def __get__(self, instance, owner):
if self.key not in instance.__dict__:
raise AttributeError, self.key
return instance.__dict__[self.key]
def __set__(self, instance, value):
if not isinstance(value, self.kind):
raise TypeError, self.kind
instance.__dict__[self.key] = value
class MyUsefulClass(object):
a = ValidatedAttribute("a", int)
But I have a few questions!
Firstly, what exactly is MyUsefulClass().a
? Is it an instance of a ValidatedAttribute class somehow bound to an instance of a MyUsefulClass
class?
And secondly, what I'd really like is something like,
class MyUsefulClass(object):
def __init__(self):
self.value = Checker(int, 99)
def Checker(self, kind, default):
# Code for verifying that associated attribute is of type kind.
and somehow bind an attribute access to some sort of intercept method, without having to use a whole other descriptor class.
I'm sorry if my understanding is lacking - I'm still getting to grips with new-style Python classes. Any help or pointers in the right direction would be much appreciated.
Upvotes: 2
Views: 1785
Reputation: 49856
Instead of writing this stuff yourself, use the traits library: https://pypi.python.org/pypi/traits. Using libraries is the pythonic approach to most problems.
However, in general, you should write code that is sufficiently generic that this sort of checking is largely unnecessary. It's easy to do in python.
Upvotes: 1
Reputation: 60167
Please, please, please do not do isinstance(new_value, int)
.
You might possibly get away with
from numbers import Integral
isinstance(new_value, Integral)
but it's completely anathema to duck typing.
Firstly, what exactly is MyUsefulClass().a? Is it an instance of a ValidatedAttribute class somehow bound to an instance of a MyUsefulClass class?
In order to find MyUsefulClass().a
, Python asks the class. The class tells it that it has a getter-setter for the attribute and tells Python to ask the attribute. The attribute says finds it and then that tells the class what the attribute is. The attribute isn't the getter-setter.
MyUsefulClass.a
is the getter-setter (property), not MyUsefulClass().a
.
And secondly, what I'd really like is something like [stuff]
AFAIK, you can only set properties on the class, not on instances, because how would you know whether you want to retrieve the property or the item the property shadows?
Upvotes: 1
Reputation: 69110
Firstly, what exactly is MyUsefulClass().a? Is it an instance of a ValidatedAttribute class somehow bound to an instance of a MyUsefulClass class?
Yes, that is it exactly.
And secondly, what I'd really like is something like [...] and somehow bind an attribute access to some sort of intercept method, without having to use a whole other descriptor class.
Well, you could overwrite __getattribute__
, as it does intecept every attribute lookup, but it is way more complicated than using descriptors.
Descriptors work for exactly what you are wanting to do (automatic validation). About the only improvement you could make is to also use a metaclass to automatically set the key for you, and then your class code look like this:
class MyUsefulClass(object):
a = ValidatedAttribute(int)
But first get comfortable with descriptors, metaclasses are complex and can be tough to grok.
Oh, and one small change I would make to your ValidatedAttribute
:
def __init__(self, key, default):
self.key = key
self.kind = type(default)
self.default = default
def __get__(self, instance, owner):
if self.key not in instance.__dict__:
return self.default
return instance.__dict__[self.key]
Upvotes: 1
Reputation: 6730
It sounds like you want to use properties
class MyUsefulClass(object):
def __init__(self):
self._value = 0
@property
def value(self):
return self._value
@value.setter
def value(self, value):
self._value = self.Checker(value, int, 99)
def Checker(self, value, kind, default):
return value if isinstance(value, kind) else default
Upvotes: 0
Reputation: 3185
I think what you're looking for are python properties. Correct approach to validate attributes of an instance of class seems relevant to your qeustion. You might also look at Python descriptor vs property
Upvotes: 0