Hello I am messing around with descriptors and I am trying to understand how they work.
So this is the stupid code I am working on:
class InputValidator:
def __set__(self, instance, value):
if value != value.lower():
raise ValueError("Please enter lowercase letters")
self._name = value
def __get__(self, instance, owner):
return self._name
class InputForm:
def __init__(self):
self.name = ""
self.start()
def start(self):
input_name = input("Please enter your name: ")
self.name = input_name
name = InputValidator()
INPUT = InputForm()
print(INPUT.name)
So if I am not mistaken that’s what descriptors should be used for, to provide some validation when an attribute is set or deleted.This is how my code should run.So first in the InputForm class we initiliaze an empty attribute self.name and then we call a function self.start().This function is going to ask the user for input and this input is going to be the value of the key “name” in the dictionary of attributes.However we want to validate this input, so we define a descriptor InputValidator which is going to check if all the letters are lowercase, in case the condition is not met a value error is raised and self.name is not succesfully saved.Now I got two questions:
How can I ask the user again for input (I would like to put something like instance.start()after raise ValueError but this is not possible).
Is this a good way to approach this problem, or is it just trash?In the latter case, could someone provide a professional and advanced answer(how would you go about this problem in the best way possible)
Thank you all for your attention and help
Related
I'm not sure whether this is a great approach to be using, but I'm not hugely experienced with Python so please accept my apologies. I've tried to do some research on this but other related questions have been given alternative problem-specific solutions - none of which apply to my specific case.
I have a class that handles the training/querying of my specific machine learning model. This algorithm is running on a remote sensor, various values are fed into the object which returns None if the algorithm isn't trained. Once trained, it returns either True or False depending on the classification assigned to new inputs. Occasionally, the class updates a couple of threshold parameters and I need to know when this occurs.
I am using sockets to pass messages from the remote sensor to my main server. I didn't want to complicate the ML algorithm class by filling it up with message passing code and so instead I've been handling this in a Main class that imports the "algorithm" class. I want the Main class to be able to determine when the threshold parameters are updated and report this back to the server.
class MyAlgorithmClass:
def feed_value(self):
....
class Main:
def __init__(self):
self._algorithm_data = MyAlgorithmClass()
self._sensor_data_queue = Queue()
def process_data(self):
while True:
sensor_value = self._sensor_data_queue.get()
result, value = self._algorithm_data.feed_value(sensor_value)
if result is None:
# value represents % training complete
self._socket.emit('training', value)
elif result is True:
# value represents % chance that input is categoryA
self._socket.emit('categoryA', value)
elif result is False:
...
My initial idea was to add a property to MyAlgorithmClass with a setter. I could then decorate this in my Main class so that every time the setter is called, I can use the value... for example:
class MyAlgorithmClass:
#property
def param1(self):
return self._param1
#param1.setter
def param1(self, value):
self._param1 = value
class Main:
def __init__(self):
self._algorithm_data = MyAlgorithmClass()
self._sensor_data_queue = Queue()
def watch_param1(func):
def inner(*args):
self._socket.emit('param1_updated', *args)
func(*args)
My problem now, is how do I decorate the self._algorithm_data.param1 setter with watch_param1? If I simply set self._algorithm_data.param1 = watch_param1 then I will just end up setting self._algorithm_data._param1 equal to my function which isn't what I want to do.
I could use getter/setter methods instead of a property, but this isn't very pythonic and as multiple people are modifying this code, I don't want the methods to be replaced/changed for properties by somebody else later on.
What is the best approach here? This is a small example but I will have slightly more complex examples of this later on and I don't want something that will cause overcomplication of the algorithm class. Obviously, another option is the Observer pattern but I'm not sure how appropriate it is here where I only have a single variable to monitor in some cases.
I'm really struggling to get a good solution put together so any advice would be much appreciated.
Thanks in advance,
Tom
Use descriptors. They let you customize attribute lookup, storage, and deletion in Python.
A simplified toy version of your code with descriptors looks something like:
class WatchedParam:
def __init__(self, name):
self.name = name
def __get__(self, instance, insttype=None):
print(f"{self.name} : value accessed")
return getattr(instance, '_' + self.name)
def __set__(self, instance, new_val):
print(f"{self.name} : value set")
setattr(instance, '_' + self.name, new_val)
class MyAlgorithmClass:
param1 = WatchedParam("param1")
param2 = WatchedParam("param2")
def __init__(self, param1, param2, param3):
self.param1 = param1
self.param2 = param2
self.param3 = param3
class Main:
def __init__(self):
self._data = MyAlgorithmClass(10, 20, 50)
m = Main()
m._data.param1 # calls WatchedParam.__get__
m._data.param2 = 100 # calls WatchedParam.__set__
The WatchedParam class is a descriptor and can be used in MyAlgorithmClass to specify the parameters that need to be monitored.
The solution I went for is as follows, using a 'Proxy' subclass which overrides the properties. Eventually, once I have a better understanding of the watched parameters, I won't need to watch them anymore. At this point I will be able to swap out the Proxy for the base class and continue using the code as normal.
class MyAlgorithmClassProxy(MyAlgorithmClass):
#property
def watch_param1(self):
return MyAlgorithmClass.watch_param1.fget(self)
#watch_param1.setter
def watch_param1(self, value):
self._socket.emit('param1_updated', *args)
MyAlgorithmClass.watch_param1.fset(self, value)
I have a question to ask, please. Given the code below, can you please let me know why in manager (or in the worker) class why
self.FirstName
gives the same result as
self._firstName
I would have thought that self._firstName would not be accessible in either of the classes (Manager/Worker) since it local to the Employee class and should not be accessible outside it, no ?
Please suggest.
import gc
class Employee(object):
"""Employee Base Class"""
def __init__(self, FirstName, LastName,Age, Role):
super(Employee, self).__init__()
self._firstName = FirstName
self._lastName = LastName
self._age = Age
self._role = Role
#property
def FirstName(self):
return self._firstName
#property
def Age(self):
return self._age
#property
def Role(self):
return self._role
#FirstName.setter
def FirstName(self, value):
self._firstName = value;
pass
#Role.setter
def Role(self, value):
self._role = value;
pass
class Manager(Employee):
"""Manager class"""
def __init__(self, FirstName,LastName,Age):
Employee.__init__(self,FirstName, LastName, Age, 'Manager')
# super(Manager, self).__init__()
def getParents(self):
"""Get parents of the class"""
print(gc.get_referrers(self))
pass
def ManagerInfo(self):
print("FirstName : " + self.FirstName)
print("Role : " + self.Role)
print("Age : " + str(self.Age))
class Worker(Employee):
"""docstring for Worker"""
def __init__(self, FirstName, LastName, Age):
Employee.__init__(self,FirstName, LastName, Age, 'employee')
def getParents(self):
"""Get parents of the class"""
print(gc.get_referrers(self))
pass
def WorkerInfo(self):
print("FirstName : " + self.FirstName)
print("Role : " + self.Role)
print("Age : " + str(self.Age))
pass
# manager = Employee('John','Doe' , 40, 'Manager')
# print("{0}'s age is {1} years.".format(manager.FirstName, manager.Age))
anEmp = Worker('WorkerName', 'LastName', 20)
aManager = Manager('John', 'Doe', 40)
print(anEmp.WorkerInfo())
print(anEmp.getParents())
print("----------------------------")
print(aManager.ManagerInfo())
print(aManager.getParents())
Thanks
why self.FirstName gives the same result as self._firstName
Because you defined FirstName as a property returning self._firstname. What did you expect actually ?
I would have thought that self._firstName would not be accessible in either of the classes (Manager/Worker) since it local to the Employee class
It's not 'local to the Employee class', it's an attribute of Employee instances (it doesn't exist in the Employee class itself).
and should not be accessible outside it, no ?
While prefixing a name with a single underscore denotes an implementation attribute (IOW something that is NOT part of the public API - the equivalent of 'protected' in most mainstream languages), it doesn't prevent access to the attribute. Actually there's absolutely NO enforcement of access restriction in Python, it's all convention (and eventually name mangling for __pseudoprivates names).
Python's philosophy is that we are all consenting adults and are wise enough to not do stupid things like messing with what is clearly labelled as an implementation attribute without accepting full responsability for breaking encapsulation.
can you please let me know what I should be doing in order to make sure that the user can only set the value using the setters and not by doing self._firstName
Nothing more than you already did actually. Re-read the above paragraphs, I already mentionned that Python did NOT enforced access restriction of any kind. self._firstname is prefixed with a single leading underscore, which is the way to tell "this is an implemention detail and not part of the API, you should not be messing with this attribute, you should not even know it exists, so if you break something by messing with it well too bad for you dude, but you're on your own".
so if in case, I have some arbitrary logic that manipulates the value in the setter before setting it, the updated value will not be available if the user just does self._firstName instead of self.FirstName
The chances this would happen are rather low actually (and that's an understatement) but theoritically yes this could happen. But this is totally unrelated since you'd have the very same problem if the user used self.FirstName instead since it would still return the stale value...
I'm writing a wrapper around a REST API, and I want to allow the user to retrieve a widget either by its name or by its database id.
So I made both keyword arguments. Now, it seems like you should only be able to pass in exactly one of them. That's not too bad to implement, though I'm not sure if it could be more readable.
def get_widget(id=None, name=None):
if (id and name) or not (id or name):
raise ValueError("Exactly one of (id, name) is required.")
# do stuff
I'm curious: what should I do if I had n of these keyword arguments? I could take in **kwargs and enforce len(kwargs) == 1, but then I would need to iterate over kwargs in order to get the key, and then switch on the key.
Is there a better or more readable way?
Honestly, I'd just take this approach:
def get_widget_by_id(id): pass # your code here
def get_widget_by_name(name): pass # your code here
In general, though, I'm not aware of any way to just allow one of two arguments without rolling your own check and exception. I think the cleanest way to write that would look something like this:
def get_widget(*, arg_one=None, arg_two=None): # use the * to force keyword args
if arg_one is None and arg_two is None:
raise ValueError("No args provided."
" Must provide either 'arg_one' or 'arg_two'")
elif arg_one is not None and arg_two is not None:
raise ValueError("Too many args provided."
" Must provide only 'arg_one' or 'arg_two'")
else:
# do your thing
If you have a large number of arguments, you could use any/all
if all(_ is None for _ in (arg_one, arg_two)):
pass
elif all(_ is not None for _ in (arg_one, arg_two)):
pass
As yet another alternative, you could always just allow both id and name, redundant though it may be, and only return the widget if it matche both.
The "common" case is to write multiple functions (get_widget_by_id, get_widget_by_name, etc.), and then redirect them to an internal _get_widget function so the code can be properly shared.
You could also make a union-type object using a class, but this might be more work, and potentially look strange to callers:
class WidgetKey(object):
def __init__ (self):
self._id = None
self._name = None
#property
def id (self):
return self._id
#id.setter
def id (self, val):
self._id = val
self._name = None
#property
def name (self):
return self._name
#name.setter
def name (self, val):
self._name = val
self._id = None
E.g. just reset all other values when someone sets any one. This is a little obtuse (and could be written cleaner), but if you have a huge set of arguments, this is sometimes useful.
In my Student subclass below, I am calculating average GPA from manually inputted grades stored in the self.courses dict attribute as {course:grade}.
The user should be able to enter in the console >>>print(fred.gpa),given fred is a proper Student instance with grades in self.courses, and should get 3.8 (for example) printed to the console.
However, 3.8 does not print to console, rather <bound method Student.gpa of <college.Student object at 0x7ff55e122ad0>>
I understand that this is the result of printing a function, but I want to print just a number using just print(fred.gpa) and not fred.gpa()
Does this mean I have to convert the output of gpa.Student into a string?
Here is my code for ref:
def __init__(self, name, cid, email):
self.courses = {}
super().__init__(name, cid, email)
def add_course(self, course):
if course in self.courses:
# duplicate courses not allowed
raise ValueError
print("student is already registered for this course")
else:
self.courses.update({course:0})
return self
def update_grade(self, course, grade):
if course not in self.courses:
# student must be registered in class to receive grade
raise ValueError
print("student is not registered for this course")
else:
self.courses[course] = float(grade)
return self
def gpa(self):
grades = list(self.courses.values())
totGPA = sum(grades)/len(grades)
return str(totGPA)
What you need is something that will let you implement something as a method, but access it as a non-method attribute. That turns out to be quite easy - it's called a property, and it works like this:
class Student:
#property
def gpa(self):
# Rest of the implementation, unchanged
Ta-da!
Note that you can fix up your implementation of gpa a little: sum can take any iterable (it doesn't have to be a list), and dicts (and their keys, values and items views) have a length, so you can do:
#property
def gpa(self):
return sum(self.courses.values())/len(self.courses)
I've omitted your call to str, since it seems from your question that that was a first attempt to fix your problem. You could reinstate it if you need it there for some other reason.
I am quite new to Python and Django, and totally new on Stack Overflow, so I hope I won't break any rules here and I respect the question format.
I am facing a problem trying to implement a custom model field with Django (Python 3.3.0, Django 1.5a1), and I didn't find any similar topics, I am actually quite stuck on this one...
So there is a Player, he has got a Hand (of Card). The Hand inherits from CardContainer, which is basically a list of cards with some (hidden here) helper functions.
Here is the corresponding code:
from django.db import models
class Card:
def __init__(self, id):
self.id = id
class CardContainer:
def __init__(self, cards=None):
if cards is None:
cards = []
self.cards = cards
class Hand(CardContainer):
def __init__(self, cards=None):
super(Hand, self).__init__(cards)
class CardContainerField(models.CommaSeparatedIntegerField):
__metaclass__ = models.SubfieldBase
def __init__(self, cls, *args, **kwargs):
if not issubclass(cls, CardContainer):
raise TypeError('{} is not a subclass of CardContainer'.format(cls))
self.cls = cls
kwargs['max_length'] = 10
super(CardContainerField, self).__init__(*args, **kwargs)
def to_python(self, value):
if not value:
return self.cls()
if isinstance(value, self.cls):
return value
if isinstance(value, list):
return self.cls([i if isinstance(i, Card) else Card(i) for i in value])
# String: '1,2,3,...'
return self.cls([Card(int(i)) for i in value.split(',')])
def get_prep_value(self, value):
if value is None:
return ''
return ','.join([str(card.id) for card in value.cards])
class Player(models.Model):
hand = CardContainerField(Hand)
But when I get a player, lets say, like this: Player.objects.get(id=3).hand, instead of getting a Hand instance (or even a CardContainer instance at all!), I am just getting a comma-separated string of integers like "1,2,3", which is fine in the database (it is the format I'd like to see IN the database)...
It seems to me that to_python doesn't get called, so the returned data is the raw value, hence the string. When I searched for this type of problems, people missed the __metaclass__ = models.SubfieldBase... I hoped I could have missed that too but, hey, it would have been too simple!
Did I miss something trivial, or am I wrong for the whole thing? :D
Thanks a lot!!
In python 3 the module-global __metaclass__ variable is no longer supported. You must use:
class CardContainerField(models.CommaSeparatedIntegerField, metaclass=models.SubfieldBase):
...
for Django 1.10 and latest
class CardContainerField(models.CommaSeparatedIntegerField):
def from_db_value(self,value, expression, connection, context):
.......