I understand that in Python variables point to objects so when you assign one to another they then both point to the same object. What I'd like to do is to make one variable change when the other one does. In the case I am working on a GUI. So I have a label with an attribute for its text. I'd like that attribute to be equal to an attribute in another class. At the moment I am doing it by using an intermediate function but it feels like there should be a more elegant way of doing it. So my way is effectively similar to the below:
class Label():
def init():
self.text = None
self.gettext = None
def display():
if callable(self.gettext):
self.text = self.gettext()
else:
self.text = self.gettext
print(str(self.text))
class Anotherclass():
def init():
self.anattribute = "avaluethatchanges"
mylabel = Label()
myclass = Anotherclass()
def gettheattribute():
return myclass.anattribute
mylabel.gettext = gettheattribute
There will be lots of labels linked to lots of different classes. So what I would like to be able to do is just:
mylabel.gettext = myclass.anattribute
However, when myclass.anattribute gets changed - myclass.gettext doesn't. I understand why but is there another way of writing it so that it does - without creating the function?
Many thanks
EDIT: - Both classes will be used in other applications where one or the other might not exist so I can't hard code the relationship between them within the classes themselves.
The first thing I would say is that it's somewhat of an antipattern to duplicate the storage of data in two places, since it violates the DRY principle of software development.
Generally, with GUI designs like this, there's the concept of MVC, or Model, View, Controller.
It's a pretty large topic, but the general idea is that you create a model object to store all your data, and then all the other parts of the GUI -- the many Views that display the data, and the Controllers that change the data -- all look at the model, so that the data is only stored and updated in one place.
GUI elements are either designed to accept a model and refreshes are either manually triggered or there is some type of Listen/Callback/Event system to automatically trigger refreshes on the Views when the model changes. The specific way to handle that depends on the GUI framework you are using.
One simple way to implement this would be to create a model class that both classes share and use python properties and a callback registry to trigger updates.
class Model():
def __init__(self, text):
self._text = text
self._callbacks = []
def on_text_changed(callback):
self._callbacks.append(callback)
#property
def text(self):
return self._text
#text.setter
def text(self, value):
self._text = text
for callback in self._callbacks:
callback()
Then both other classes would need something like this
class Label():
def __init__(self, model):
self.model = model
self.model.on_text_changed(self.refresh)
def refresh(self):
print(self.text)
#property
def text(self):
return self.model.text
#text.setter
def text(self, value):
self.model.text = value
Then you would create them like this
model = Model('The text')
label = Label(model)
another_class = AnotherClass(model)
label.text = 'This will update text on all classes'
another_class.text = 'So will this'
model.text = "And so will this.
Sounds like this might be a good use case for a property. Properties let you have getter/setters that work seamlessly like attributes. From the docs
[a property is] a succinct way of building a data descriptor that triggers function calls upon access to an attribute
...
The property() builtin helps whenever a user interface has granted attribute access and then subsequent changes require the intervention of a method.
mylabel = Label()
class MyClass(object):
def __init__(self, some_label):
self._anattribute = None
self.label = some_label
#property
def anattribute(self):
return self._anattribute
#anattribute.setter
def anattribute(self, value):
self._anattribute = value # set the underlying value
# do something else, too
self.label.text = self._anattribute
So...
mylabel = Label()
myinstance = MyClass(mylabel)
myinstance.anattribute = 'foo'
mylabel.text == 'foo' # True
Storing self._anattribute is not strictly necessary, either. You could have the getter/setter access/modify self.label.text directly, if applicable.
class MyClass:
def __init__(self,shared_dict): # use some mutable datatype (a dict works well)
self.shared = shared_dict
def __getattr__(self,item):
return self.shared.get(item)
data = {'a':'hello','b':[1,2,3]}
c = MyClass(data)
print(c.a)
data['a'] = 'world!'
print(c.a)
I guess ... this doesnt make much sense from a use case standpoint really ... there is almost guaranteed to be a better way to do whatever it is you are actually trying to do (probably involves notifying subscribers and updating variables)
see it in action here https://repl.it/repls/TestyGruesomeNumbers
Sounds like you have some GUI element that you want to tie to an underlying model. Riffing on 0x5453's very good advice, what about the following:
class Label():
def __init__(self, text_source, source_attribute):
self.text_source = text_source
self.source_attribute = source_attribute
#property
def text_from_source(self):
return getattr(self.text_source, self.source_attribute)
def display(self):
print(str(self.text_from_source))
class Anotherclass():
def __init__():
self.anattribute = "avaluethatchanges"
>>> A = Anotherclass()
>>> L = Label(A, "anattribute")
>>> L.display()
avaluethatchanges
>>> A.anattribute = 3.1415
>>> L.display()
3.1415
This does not let you change the attribute from within the label, but I'd prefer it that way.
Related
I currently have the following two ways:
class Venue:
store = Database.store()
ids = [vid for vid in store.find(Venue.id, Venue.type == "Y")]
def __init__(self):
self.a = 1
self.b = 2
OR
class Venue:
#classmethod
def set_venue_ids(cls):
store = Database.store()
cls.ids = [vid for vid in store.find(Venue.id, Venue.type == "Y")]
def __init__(self):
self.a = 1
self.b = 2
And before using/instantiating the class I would call:
Venue.set_venue_ids()
What would be the correct way of achieving this?
If it's the first way, what would I do if the instantiation of the attribute required more complex logic that could be done more simply through the use of a function?
Or is there an entirely different way to structure my code to accomplish what I'm trying to do?
From a purely technical POV, a class is an instance of its metaclass so the metaclass initializer is an obvious candidate for class attributes initialization (at least when you have anything a bit complex).
Now given the canonical lifetime of a class object (usually the whole process), I would definitly not use an attribute here - if anyone adds or removes venues from your database while your process is running, your ids attributes will get out of sync. Why don't you use a classmethod instead to make sure your data are always have up to date ?
Oh and yes, another way to construct your Venue.ids (or any other class attribute requiring non-trivial code) without having complex code at the class top-level polluthing the class namespace (did you noticed that in your first example store becomes a class attributes too, as well as vid if using Python 2.x ?) is to put the code in a plain function and call that function from within your class statement's body, ie:
def list_venue_ids():
store = Database.store()
# I assume `store.find()` returns some iterator (not a `list`)
# if it does return a list, you could just
# `return store.find(...)`.
return list(store.find(Venue.id, Venue.type == "Y"))
class Venue(object):
ids = list_venue_ids()
def __init__(self):
self.a = 1
self.b = 2
I'm new to classes, this is a small piece of code I've written, but I'm still really shaky on this concept, and am wondering exactly how the method node_name comes into play here and if it's even needed?
from rdflib import BNode
class HigherNode(object):
def node_name(name):
return name
def __init__(self, **kwargs):
self.node_type = kwargs.get('node_type', 'cog_con')
self.position = kwargs.get('position', 0)
self.node_id = self.node_name
self.node = kwargs.get(self.node_name(), BNode())
for key, value in kwargs.items():
setattr(self, key, value)
def __str__(self):
return 'This is the node of {} in the graph'.format(self.node_id)
I behavior that I'm seeking is something equivalent to this:
elephant = BNode()
when used as:
some_node = HigherNode(node_id = 'elephant')
So, first off, methods have to be called by an instance of the class. So, your behavior would look something like this:
# create an instance
node = HigherNode()
# get the name
print node.node_name()
However, you never declared name inside the class. So, you'll have to do something like this:
def node_name(self):
return self.name
(All instances pass a reference to themselves to thier functions when called, so you'll always have to have at least one variable in the function call. You don't have to call it self.)
Really, it looks like what you want is actually a name setter/getter.
Try this:
Declare/set the variable in __init__.
def __init__(self, **kwargs):
self.node_name= kwargs.get('node_name', None)
Then you can use the variable like this:
# create an instance
node = HigherNode()
# get the name
print node.node_name
# set the name
node.node_name = "bluh"
Since your class extends object, use getter/setter properties.
#property
def node_name(self):
return self.node_name
#node_name.setter
def node_name(self, x):
self.node_name = str(x)
These are called exactly the same as above in option 1:
# create an instance
node = HigherNode()
# get the name
print node.node_name
# set the name
node.node_name = "bluh"
I prefer this method, since it allows you much more control over how things are set, or even whether or not you can set or get them! (Just make a getter property without a corresponding setter property, for instance.)
However, this second method is more work to set up and may not be suitable for simple variables.
I have a series of functions that serve to classify data. Each function is passed the same input. The goal of this system is to be able to drop in new classification functions at will without having to adjust anything.
To do this, I make use of a classes_in_module function lifted from here. Then, every classifier in one python file will be ran on each input.
However, I am finding that implementing the classifier as either a class or a function is kludgy. Classes mean instantiating and executing, while functions lack clean introspection to allow me to query the name or use inheritance to define common values.
Here is an example. First, the class implementation:
class AbstractClassifier(object):
#property
def name(self):
return self.__class__.__name__
class ClassifierA(AbstractClassifier):
def __init__(self, data):
self.data = data
def run(self):
return 1
This can then be used in this fashion, assuming that classifier_list is the output of classes_in_module on a file containing ClassifierA among others:
result = []
for classifier in classifier_list:
c = classifier(data)
result.append(c.run())
However, this seems a bit silly. This class is obviously static, and doesn't really need to maintain its own state, as it is used once and discarded. The classifier is really a function, but then I lose the ability to have a shared name property -- I would have to use the ugly introspection technique sys._getframe().f_code.co_name and replicate that code for each classifier function. And any other shared properties between classifiers would also be lost.
What do you think? Should I just accept this mis-use of classes? Or is there a better way?
Functions can have member data. You can also find the name of a function using the func_name attribute.
def classifier(data):
return 1
classifier.name = classifier.func_name
print(classifier.name) #classifier
If you want multiple functions to behave the same way, you can use a decorator.
function_tracker = []
def add_attributes(function):
function.name = function.func_name
function.id = len(function_tracker)
function_tracker.append(function)
return function
#add_attributes
def classifier(data):
return 1
print(classifier.name, classifier.id) # 'classifier', 0
Would this work to avoid classes in your specific case?
If you don't need several instances of the class (and it seems you don't) make one instance of the class and change the run to __call__:
class AbstractClassifier(object):
#property
def name(self):
return self.__class__.__name__
class ClassifierA(AbstractClassifier):
def __call__(self, data):
return 1
ClassifierA = ClassifierA() # see below for alternatives
and then in your other code:
result = []
for classifier in classifier_list:
result.append(classifier(data))
Instead of having ClassifierA = ClassifierA() (which isn't very elegant), one could do:
classifier_list = [c() for c in (ClassifierA, ClassifierB, ...)]
This method allows you to keep your classes handy should you need to create more instances of them; if you don't ever need to have more than one instance you could use a decorator to IAYG (instantiate as you go ;) :
def instantiate(cls):
return cls()
#instantiate
class ClassifierZ(object):
def __call__(self, data):
return some_classification
To use a class instance as a function:
class ClassifierA(AbstractClassifier):
def __init__(self, data):
self.data = data
def __call__(self):
return 1
result = []
for classifier in classifier_list:
c = classifier(data)
result.append(c())
Or to just use functions:
classifier_list = []
def my_decorator(func):
classifier_list.append(func)
return func
#my_decorator
def classifier_a(data):
return 1
result = []
for classifier in classifier_list:
c = classifier(data)
result.append(c)
class ThirdPartyA(object):
def __init__(self):
...
def ...():
...
-------------------
from xxx import ThirdPartyA
class ThirdPartyB(object):
def a(self):
...
#call to ThirdPartyA
....
def b(self):
...
#call to ThirdPartyA
...
def c(self):
...
#call to ThirdPartyA
...
-----------------------------------
from xxx import ThirdPartyA
class MyCodeA(ThirdPartyA):
def __init__(self):
# overriding code
When overriding the __init__ method of A class, how could I instruct B class that it should call MyCodeA instead of ThirdPartyA in all its methods?
The real code is here:
CLass Geoposition: ThirdPartyA
Class GeopositionField: ThirdPartyB
My override to class Geoposition so it returns max 5 decimal digits:
class AccuracyGeoposition(Geoposition):
def __init__(self, latitude, longitude):
if isinstance(latitude, float) or isinstance(latitude, int):
latitude = '{0:.5f}'.format(latitude)
if isinstance(longitude, float) or isinstance(longitude, int):
longitude = '{0:.5f}'.format(longitude)
self.latitude = Decimal(latitude)
self.longitude = Decimal(longitude)
From your updated code, I think what you're trying to do is change GeopositionField. to_python() so that it returns AccuracyGeoposition values instead of Geoposition values.
There's no way to do that directly; the code in GeopositionField explicitly says it wants to construct a Geoposition, so that's what happens.
The cleanest solution is to subclass GeopositionField as well, so you can wrap that method:
class AccuracyGeopositionField(GeopositionField):
def topython(self, value):
geo = super(AccuracyGeopositionField, self).topython(value)
return AccuracyGeoposition(geo.latitude, geo.longitude)
If creating a Geoposition and then re-wrapping the values in an AccuracyGeoposition is insufficient (because accuracy has already been lost), you might be able to pre-process things before calling the super method as well/instead. For example, if the way it deals with list is not acceptable (I realize that's not true here, but it serves as a simple example), but everything else you can just let it do its thing and wrap the result, you could do this:
class AccuracyGeopositionField(GeopositionField):
def topython(self, value):
if isinstance(value, list):
return AccuracyGeoposition(value[0], value[1])
geo = super(AccuracyGeopositionField, self).topython(value)
return AccuracyGeoposition(geo.latitude, geo.longitude)
If worst comes to worst, you may have to reimplement the entire method (maybe by copying, pasting, and modifying its code), but hopefully that will rarely come up.
There are hacky alternatives to this. For example, you could monkeypatch the module to globally replace the Geoposition class with your AccuracyGeoposition class But, while that may save some work up front, you're almost certain to be unhappy with it when you're debugging things later. Systems that are designed for aspect-oriented programming (which is basically controlled monkeypatching) are great, but trying to cram it into systems that were designed to resist it will give you headaches.
Assuming your real code works like your example—that is, every method of B creates a new A instance just to call a method on it and discard it—well, that's a very weird design, but if it makes sense for your use case, you can make it work.
The key here is that classes are first-class objects. Instead of hardcoding A, store the class you want as a member of the B instance, like this:
class B(object):
def __init__(self, aclass=A):
self.aclass = aclass
def a(self):
self.aclass().a()
Now, you just create a B instance with your subclass:
b = B(OverriddenA)
Your edited version does a different strange thing: instead of constructing a new A instance each time to call methods on it, you're calling class methods on A itself. Again, this is probably not what you want—but, if it is, you can do it:
class B(object):
def __init__(self, aclass=A):
self.aclass = aclass
def a(self):
self.aclass.a()
However, more likely you don't really want either of these. You want to take an A instance at construction time, store it, and use it repeatedly. Like this:
class B(object):
def __init__(self, ainstance):
self.ainstance = ainstance
def a(self):
self.ainstance.a()
b1 = B(A())
b2 = B(OverriddenA())
If this all seems abstract and hard to understand… well, that's because we're using meaningless names like A, B, and OverriddenA. If you tell us the actual types you're thinking about, or just plug those types in mechanically, it should make a lot more sense.
For example:
class Vehicle(object):
def move(self):
print('I am a vehicle, and I am moving')
class Operator(object):
def __init__(self, vehicle):
self.vehicle = vehicle
def move(self):
print('I am moving my vehicle')
self.vehicle.move()
class Car(object):
def move(self):
print('I am a car, and I am driving')
driver = Operator(Car())
driver.move()
In python, I can alter the state of an instance by directly assigning to attributes, or by making method calls which alter the state of the attributes:
foo.thing = 'baz'
or:
foo.thing('baz')
Is there a nice way to create a class which would accept both of the above forms which scales to large numbers of attributes that behave this way? (Shortly, I'll show an example of an implementation that I don't particularly like.) If you're thinking that this is a stupid API, let me know, but perhaps a more concrete example is in order. Say I have a Document class. Document could have an attribute title. However, title may want to have some state as well (font,fontsize,justification,...), but the average user might be happy enough just setting the title to a string and being done with it ...
One way to accomplish this would be to:
class Title(object):
def __init__(self,text,font='times',size=12):
self.text = text
self.font = font
self.size = size
def __call__(self,*text,**kwargs):
if(text):
self.text = text[0]
for k,v in kwargs.items():
setattr(self,k,v)
def __str__(self):
return '<title font={font}, size={size}>{text}</title>'.format(text=self.text,size=self.size,font=self.font)
class Document(object):
_special_attr = set(['title'])
def __setattr__(self,k,v):
if k in self._special_attr and hasattr(self,k):
getattr(self,k)(v)
else:
object.__setattr__(self,k,v)
def __init__(self,text="",title=""):
self.title = Title(title)
self.text = text
def __str__(self):
return str(self.title)+'<body>'+self.text+'</body>'
Now I can use this as follows:
doc = Document()
doc.title = "Hello World"
print (str(doc))
doc.title("Goodbye World",font="Helvetica")
print (str(doc))
This implementation seems a little messy though (with __special_attr). Maybe that's because this is a messed up API. I'm not sure. Is there a better way to do this? Or did I leave the beaten path a little too far on this one?
I realize I could use #property for this as well, but that wouldn't scale well at all if I had more than just one attribute which is to behave this way -- I'd need to write a getter and setter for each, yuck.
It is a bit harder than the previous answers assume.
Any value stored in the descriptor will be shared between all instances, so it is not the right place to store per-instance data.
Also, obj.attrib(...) is performed in two steps:
tmp = obj.attrib
tmp(...)
Python doesn't know in advance that the second step will follow, so you always have to return something that is callable and has a reference to its parent object.
In the following example that reference is implied in the set argument:
class CallableString(str):
def __new__(class_, set, value):
inst = str.__new__(class_, value)
inst._set = set
return inst
def __call__(self, value):
self._set(value)
class A(object):
def __init__(self):
self._attrib = "foo"
def get_attrib(self):
return CallableString(self.set_attrib, self._attrib)
def set_attrib(self, value):
try:
value = value._value
except AttributeError:
pass
self._attrib = value
attrib = property(get_attrib, set_attrib)
a = A()
print a.attrib
a.attrib = "bar"
print a.attrib
a.attrib("baz")
print a.attrib
In short: what you want cannot be done transparently. You'll write better Python code if you don't insist hacking around this limitation
You can avoid having to use #property on potentially hundreds of attributes by simply creating a descriptor class that follows the appropriate rules:
# Warning: Untested code ahead
class DocAttribute(object):
tag_str = "<{tag}{attrs}>{text}</{tag}>"
def __init__(self, tag_name, default_attrs=None):
self._tag_name = tag_name
self._attrs = default_attrs if default_attrs is not None else {}
def __call__(self, *text, **attrs):
self._text = "".join(text)
self._attrs.update(attrs)
return self
def __get__(self, instance, cls):
return self
def __set__(self, instance, value):
self._text = value
def __str__(self):
# Attrs left as an exercise for the reader
return self.tag_str.format(tag=self._tag_name, text=self._text)
Then you can use Document's __setattr__ method to add a descriptor based on this class if it is in a white list of approved names (or not in a black list of forbidden ones, depending on your domain):
class Document(object):
# prelude
def __setattr__(self, name, value):
if self.is_allowed(name): # Again, left as an exercise for the reader
object.__setattr__(self, name, DocAttribute(name)(value))