I'm a fully "static typed" man and a beginner in python and I want to make a datatype in python, which saves prev-next states of some objects. The structure is like this:
class PrevCurr:
def __init__(self, obj_initial):
self.previous = obj_initial
self.current = obj_initial
No problem if my obj_initial
is something like [None, None]
or so. But imagine I need to wrap this class onto the different big types, like dictionaries/lists/sets of 1000+ elements or/and user-defined classes. Then I would like to have something like this:
class PrevCurr:
def __init__(self, obj_type, *args, **kwargs):
'''make self.previous and self.orientation of the same type as obj_type'''
self.previous = ...
self.current = ...
My question is: how to be sure, that fields (or is there another name in python for them?) are set to be of the same type as the object, onto which I want to wrap? I have the idea presented above, that I may somehow to pass the type and additional info about that, like size and etc. as parameters instead of object itself to save the memory, but how then to ensure, that my fields are set to be exactly the same type as the object? afaik I cannot pass constructors as parameters in python and I couldn't find any sort of generic programming in python for that task. Or I got the idea fully wrong.
You're thinking as you would in a static-typed language like C or Java, where you have to pre-allocate memory for objects of a specific type before assigning anything to them. This is the wrong approach in python.
Instead, let's consider what you want: a class that can represent any single type, but which, once initialized, can only represent that particular type. In Java, you would use a generic for this, but python doesn't have those. What we can do in python is to make sure that only objects of the correct type can be assigned to it.
The idiomatic way of doing something like this in python is throwing an error at runtime if the programmer uses the class incorrectly; there's not really a good way for a static typechecker to throw a compile-time error, like it might in Java. I present the following for your consideration:
class PrevCurr:
@property
def previous(self):
return self._previous
@previous.setter
def previous(self, value):
if not isinstance(value, self._type):
raise ValueError(f"Previous value must be of type {self._type}")
self._previous = value
@property
def current(self):
return self._current
@current.setter
def current(self, value):
if not isinstance(value, self._type):
raise ValueError(f"Current value must be of type {self._type}")
self._current = value
def __init__(self, obj_initial):
self._type = type(obj_initial)
self._previous = obj_initial
self._current = obj_initial
Here, we have two external-facing variables: previous
and current
, as you have in your current example. Because we want specific behaviors on setting these variables, we use the @property
decorator to declare them. Their actual values are held in the 'private' variables _previous
and _current
, respectively.
Upon initialization, we check the type of the initial object, and store that type to the class as the 'private' variable _type
.
Then, each time something (even the class's instance itself) tries to set instance.previous
or instance.current
, we redirect it to the appropriate function. In this function, we check whether the object to be set is the same type as what we initialized the class with. If not, we throw an error.
If you're storing, for example, a list or other collection, then I don't think there's any reasonable way to ensure that the entire list remains the same type (or, indeed, to make any assumption about the contents of the list, since python itself doesn't. They're all <type 'list'>
).
One possible workaround would be to use metaclasses and overriding the __instancecheck__()
metaclass method, to create a subclass of dict
that only holds a specific type of object, and then initialize your PrevCurr
instance with one of those.
I presume from the name of the class that you have some method .update()
that copies over the current
value to previous
, and assigns a new current
value that must be the same type. In that case you might want to play around with the getters and setters to make assigning directly to previous
harder.
Example of usage:
>>> a = PrevCurr(3)
>>> a.previous = 2
>>> a.previous = 4.5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 8, in previous
ValueError: Previous value must be of type <class 'int'>
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments