The goal of this question is to determine whether or not I can wrap setting an object's attribute, without just writing a setter and then wrapping the setter.
I am trying to implement an Observer pattern and I don't want to write more code than I need to (so of course I'll write a big long StackOverflow question, hah - I figure the long-term payoff is worth it).
I started experimenting by trying to wrap obj.__setattr__ with a function but it did not do what I expected it would do, so now I am wondering if I can even wrap the assignment or changing of an object's attribute if I do not just write a setter.
This is what I tried:
class A(object):
pass
def wrapper(obj, func):
def inner(*args, **kwargs):
print "called it"
return func(*args, **kwargs)
return inner
Stick = A()
Stick.__setattr__ = wrapper(Stick, Stick.__setattr__)
Stick.x = 14 #does not print "called it"
If I just write a setter it would make for an easy hook, something like:
class A(object):
def __init__(self, x):
self.x = x
def set_x(self, new_x):
self.x = x
But I'd like to be able to implement the Observer pattern in such a way that whenever obj.x changes for any reason, the listener updates. If obj.x is an int for example I could be either setting it outright or using obj.x += some_int to set it, and so I'm wondering if there is a way of wrapping any/all setting of obj.x without, for example, writing obj.set_x(), obj.add_to_x(), obj.subtract_from_x(), obj.times_x(), etc etc.
EDIT: Thanks for pointing me at properties, but I don't see how it can help me to wrap this in the manner I'm implementing thus far.
For example, if I have an object as such:
class Foo(object):
def __init__(self, ex):
self._x = ex
#property
def x(self):
return self._x
#x.setter
def x(self, value):
self._x = value
...that's fine, and I see that I can modify the function #x.setter wraps directly, but I was hoping to create something which I can use in the following (imaginary pseudocode) way:
A.x.setter = observing_function(A, A.x.setter)
...such that when A.x changes, observing_function is called and does what it is going to do.
If it informs the answer at all -- I am working on a 'scoreboard' to display the score and lives of a central character in a video game. Instead of constantly checking during each loop or setting over and over (which I'm doing now, which seems excessive), I just want it to actually fire when the character's score/lives change, and a wrapper seemed the nicest way to do that.
I was hoping to avoid this:
def add_to_score(self, pts):
self.score += pts
scoreboard_object.text = self.score
...because that to me is awful, even though it'd work. Also it seems like a lot of code just to establish an observer pattern for two variables :P
But this is why I'd prefer to wrap the setter after the fact. I have two different objects that don't necessarily need to have hard-coded data about each other; just a player object with a self.score attribute and a scoreboard object with a self.text attribute, and while writing the 'glue' between them is of course necessary, I'd hoped writing methods to set_score and set_text wouldn't be crucial just to implement an Observer pattern.
If ultimately I can't skip writing a setter (or setters), then I suppose I'll go ahead and do it that way; I'd just hoped to avoid it.
And really while this is now a very specific example, I am also asking in a generic sense as well, because it seems really handy to be able to just watch an attribute for changes instead of coding around every attribute to be ready for maybe watching some changes of some other object. :/
Special methods, descriptors, and all other ways of mucking with attribute access only work on classes. You can't override them on objects.
Use an event (or pub/sub or whatever we're calling it now) system. Make the character responsible for emitting events, but not for remembering specifically who wants them.
class ScoreChanged(Event):
...
class Character(ListenerMixin):
#property
def score(self):
return self._score
#score.setter
def score(self, new):
old = self._score
self._score = new
self.fire_event(ScoreChanged(self, old, new))
protag = Character()
scoreboard = Scoreboard()
scoreboard.listen_event(protag, ScoreChanged, scoreboard.on_score_change)
protag.score += 10
The objects necessarily get entangled somewhere, but it becomes an implementation detail rather than a major part of your code. In particular, the scoreboard doesn't have to be a global, and the character still works fine even when the scoreboard doesn't exist.
There are examples of implementations and existing libraries on this question, but if you're writing a game, it's possible you're already using a library that has its own built in. I know pyglet does.
Special functions of new-style class instances are looked up against their class not the instance, so:
class A(object):
pass
def wrapper(func):
def inner(*args, **kwargs):
print "called it"
return func(*args, **kwargs)
return inner
Stick = A()
A.__setattr__ = wrapper(A.__setattr__)
Stick.x = 14 # prints "called it"
Stick.x *= 2 # also prints "called it"
Related
I'm trying to add flexibility to a python class, so that it notices when one of the init arguments is already an instance of that class. Skip "Initial situation" if you don't mind, how I got here.
Initial situation
I have this class:
class Pet:
def __init__(self, animal):
self._animal = animal
#property
def present(self):
return "This pet is a " + self._animal
...
and there are many functions which accept an instance of this class as an argument (def f(pet, ...)). Everything worked as expected.
I then wanted to add some flexibility to the usage of these functions: if the caller passes a Pet instance, everything keeps on working as before. In all other cases, a Pet instance is created. One way to achieve that, is like this:
def f(pet_or_animal, ...):
if isinstance(pet_or_animal, Pet): #Pet instance was passed
pet = pet_or_animal
else: #animal string was passed
pet = Pet(pet_or_animal)
...
This also works as expected, but these lines are repeated in every function. Not DRY, not good.
Goal
So, I'd like to extract the if/else from each of the functions, and integrate it into the Pet class itself. I tried changing its __init__ method to
class PetA: #I've changed the name to facilitate discussion here.
def __init__(self, pet_or_animal):
if isinstance(pet_or_animal, PetA):
self = pet_or_animal
else:
self._animal = pet_or_animal
...
and start each function with
def f(pet_or_animal, ...):
pet = PetA(pet_or_animal)
...
However, that is not working. If a Pet instance is passed, everything is good, but if a string is called, a Pet instance is not correctly created.
Current (ugly) solution
What is working, is to add a class method to the class, like so:
class PetB: #I've changed the name to facilitate discussion here.
#classmethod
def init(cls, pet_or_animal):
if isinstance(pet_or_animal, PetB):
return pet_or_animal
else:
return cls(pet_or_animal)
def __init__(self, animal):
self._animal = animal
...
and also change the functions to
def f(pet_or_animal, ...):
pet = PetB.init(pet_or_animal) #ugly
...
Questions
Does anyone know, how to change class PetA so, that it has the intended behavior? To be sure, here is the quick test:
pb1 = PetB.init('dog')
pb2 = PetB.init(pb1) #correctly initialized; points to same instance as pb1 (as desired)
pa1 = PetA('cat')
pa2 = PetA(pa1) #incorrectly initialized; pa1 != pa2
More generally, is this the right way to go about adding this flexibility? Another option I considered was writing a separate function to just do the checking, but this too is rather ugly and yet another thing to keep track of. I'd rather keep everything neat and wrapped in the class itself.
And one final remark: I realize that some people might find the added class method (petB) a more elegant solution. The reason I prefer to add to the __init__ method (petA) is that, in my real-world use, I already allow for many different types of initialization arguments. So, there is already a list of if/elif/elif/... statements that check, just which of the possibilities is used by the creator. I'd like to extend that by one more case, namely, if an initialized instance is passed.
Many thanks
I believe your current "ugly" solution is actually the correct approach.
This pushes the flexibility up as far as possible, since it is messy. Even though python allows for arbitrary types and values to float around, your users and yourself will thank you for keeping that constrained to the outermost levels.
I would think of it as (don't need to implement it this way)
class Pet:
#classmethod
def from_animal(cls, ...):
...
#classmethod
def from_pet(cls, ...):
...
#classmethod
def auto(cls, ...):
if is_pet(...):
return cls.from_pet(...)
def __init__(cls, internal_rep):
...
etc.
It is a code smell if you don't know whether your function is taking an object or an initializer. See if you can do processing as up-front as possible with user input and standardize everything beyond there.
You could use a function instead to get the same behaviour you want:
def make_pet_if_required(pet_or_animal):
if isinstance(pet_or_animal, PetA):
return pet_or_animal
else:
return Pet(pet_or_animal)
And then:
def f(pet_or_animal, ...):
pet = make_pet_if_required(pet_or_animal)
...
For more "beauty" you can try turning that function call into a decorator.
The property decorator is a great way to "protect" attributes one wants to set once and never change again. I usually deal with this this way (btw., following a the advice here):
self._name = 'foo'
#property
def name(self):
return self._name
so trying to set name directly yields an AttributeError.
However, I often see the following pattern:
#name.setter
def name(self, value):
self._name = value
#property
def name(self):
return self._name
which seems a little counter-intuitive, as it enables exactly what I want to avoid, and requires extra coding, i.e, theoretically
self.name = 'bar'
would suffice, although it is clear that this would be the worst way to deal with the problem.
The best explanation I can come up with is something like a message from the author saying "you should not change this attribute but if you really want to, there is a mechanism to do it without changing a 'protected' attribute". But then, python doesn't really protect attributes.
So, what's the point, which is more pythonic and why?
You're correct that there's no good reason to use a property if you're not doing anything special in the getter or setter. However, if you do want to do something special (like validate new values, or normalize them in some way), then it makes a lot of sense.
For example, this class's foo attribute will always be clamped between 0 and 1 (and non-numerical values will cause an error immediately):
class Foo:
_foo = 1.0
#foo
def probability(self):
return self._foo
#foo.setter
def foo(self, value):
if value < 0:
value = 0
elif value > 1:
value = 1
self._foo = value
An example with a trivial setter, but a complicated getter might be something like this (deferring an expensive initialization that might not be needed):
class Foo:
_foo = None
def initialize_foo(self):
self._foo = some_expensive_calculation()
#property
def foo(self):
if self._foo is None:
self.initialize_foo() # need the default value
return self._foo
#foo.setter
def foo(self, value):
self._foo = value
If the setter and getter are just directly writing and reading the protected variable, then they're pointless, and using it is not Pythonic; it's just wasting time on property overhead for each access. The attribute should just be made public and the property removed.
The advantage to properties is when you need to replace that simple public attribute with something more powerful, because you can make a property that continues to act like it should for code that was using the attribute, but performs additional work as well. Unless you have additional work though, stick with the attribute if you'd allow it to be written anyway.
Note: Technically, a getter and a setter isn't 100% equivalent to the attribute, since without a deleter, it's not behaviorally identical to the raw attribute. But enforcing non-deletability in your API is silly; developers who go around calling del obj.attr on random attributes of third-party class instances (or almost any instance really) deserve what's coming to them, and you shouldn't be defending against that nonsense at the expense of slowing down and complicating normal use patterns.
Although, I have some years of experience programming in Python every time I encounter a problem like this I'm using the built-in isinstance function. However, I'm not sure whether this is the ideomatic way of doing these kind of things in python.
So, I have a base class that most of my instances will be.
class Base():
def a(self):
return 1
I also have a slightly different class that look like this:
class Extended(Base):
def b(self):
return 2
Now, there is a third class that might have additional functionality depending on the received argument which would be instance of one of the previous classes.
class User():
def __init__(self, arg):
... # do some common work
if isinstance(arg, Extended):
...
# define more functionality which will call method 'b'
# at some point during runtime (as event handler or smth)
Is this really the way to go with Python on this trivial example or maybe I should consider changing the interface of the Base to something like:
class Base2():
supports_more_func = False
def a(self):
return 1
def b(self):
pass
class Extended2(Base2):
supports_more_func = True
def b(self):
return 2
class User():
def __init__(self, arg):
... # do some common work
if arg.supports_more_func:
...
# define more functionality which will call method 'b'
# at some point during runtime (as event handler or smth)
Which one is the better approach according to you guy and why?
Generally speaking, when doing object oriented programming, using isinstance is rarely the way to go, especially when you're in charge of designing the classes you use, because that would be breaking S.O.L.I.D. principles.
Instead you should simply design your class to have a common well defined interface and just use it. So testing for type or for a member is rarely the way to go.
The way I'd go would be:
class Base2():
def a(self):
return 1
def b(self):
pass
class Extended2(Base2):
def b(self):
# all that extra functionality that was in User.__init__()
return 2
class User():
def __init__(self, arg):
... # do some common work
arg.b()
now I guess that the part with:
# define more functionality which will call method 'b'
# at some point during runtime (as event handler or smth)
has some data and processing tightly coupled with User and not with Extended2, but I'm pretty sure there's an elegant way to give that data to arg.b() as argument.
Basically, I'd say that 99% of the time when you need to use isinstance() to do something, it means you have a design issue and there's a better way to do the same.
Here's some web-litterature on the topic:
http://canonical.org/~kragen/isinstance/
https://www.quora.com/When-is-it-acceptable-to-use-isinstance-in-Python
https://www.lynda.com/Programming-Languages-tutorials/Avoiding-isinstance/471978/502199-4.html
I was just wondering if it's considered wildly inappropriate, just messy, or unconventional at all to use the init method to set variables by calling, one after another, the rest of the functions within a class. I have done things like, self.age = ch_age(), where ch_age is a function within the same class, and set more variables the same way, like self.name=ch_name() etc. Also, what about prompting for user input within init specifically to get the arguments with which to call ch_age? The latter feels a little wrong I must say. Any advice, suggestions, admonishments welcome!
I always favor being lazy: if you NEED to initialize everything in the constructor, you should--in a lot of cases, I put a general "reset" method in my class. Then you can call that method in init, and can re-initialize the class instance easily.
But if you don't need those variables initially, I feel it's better to wait to initialize things until you actually need them.
For your specific case
class Blah1(object):
def __init__(self):
self.name=self.ch_name()
def ch_name(self):
return 'Ozzy'
you might as well use the property decorator. The following will have the same effect:
class Blah2(object):
def __init__(self):
pass
#property
def name():
return 'Ozzy'
In both of the implementations above, the following code should not issue any exceptions:
>>> b1 = Blah1()
>>> b2 = Blah2()
>>> assert b1.name == 'Ozzy'
>>> assert b2.name == 'Ozzy'
If you wanted to provide a reset method, it might look something like this:
class Blah3(object):
def __init__(self, name):
self.reset(name)
def reset(self, name):
self.name = name
I have a set of related classes that all inherit from one base class. I would like to use a factory method to instantiate objects for these classes. I want to do this because then I can store the objects in a dictionary keyed by the class name before returning the object to the caller. Then if there is a request for an object of a particular class, I can check to see whether one already exists in my dictionary. If not, I'll instantiate it and add it to the dictionary. If so, then I'll return the existing object from the dictionary. This will essentially turn all the classes in my module into singletons.
I want to do this because the base class that they all inherit from does some automatic wrapping of the functions in the subclasses, and I don't want to the functions to get wrapped more than once, which is what happens currently if two objects of the same class are created.
The only way I can think of doing this is to check the stacktrace in the __init__() method of the base class, which will always be called, and to throw an exception if the stacktrace does not show that the request to make the object is coming from the factory function.
Is this a good idea?
Edit: Here is the source code for my base class. I've been told that I need to figure out metaclasses to accomplish this more elegantly, but this is what I have for now. All Page objects use the same Selenium Webdriver instance, which is in the driver module imported at the top. This driver is very expensive to initialize -- it is initialized the first time a LoginPage is created. After it is initialized the initialize() method will return the existing driver instead of creating a new one. The idea is that the user must begin by creating a LoginPage. There will eventually be dozens of Page classes defined and they will be used by unit testing code to verify that the behavior of a website is correct.
from driver import get_driver, urlpath, initialize
from settings import urlpaths
class DriverPageMismatchException(Exception):
pass
class URLVerifyingPage(object):
# we add logic in __init__() to check the expected urlpath for the page
# against the urlpath that the driver is showing - we only want the page's
# methods to be invokable if the driver is actualy at the appropriate page.
# If the driver shows a different urlpath than the page is supposed to
# have, the method should throw a DriverPageMismatchException
def __init__(self):
self.driver = get_driver()
self._adjust_methods(self.__class__)
def _adjust_methods(self, cls):
for attr, val in cls.__dict__.iteritems():
if callable(val) and not attr.startswith("_"):
print "adjusting:"+str(attr)+" - "+str(val)
setattr(
cls,
attr,
self._add_wrapper_to_confirm_page_matches_driver(val)
)
for base in cls.__bases__:
if base.__name__ == 'URLVerifyingPage': break
self._adjust_methods(base)
def _add_wrapper_to_confirm_page_matches_driver(self, page_method):
def _wrapper(self, *args, **kwargs):
if urlpath() != urlpaths[self.__class__.__name__]:
raise DriverPageMismatchException(
"path is '"+urlpath()+
"' but '"+urlpaths[self.__class.__name__]+"' expected "+
"for "+self.__class.__name__
)
return page_method(self, *args, **kwargs)
return _wrapper
class LoginPage(URLVerifyingPage):
def __init__(self, username=username, password=password, baseurl="http://example.com/"):
self.username = username
self.password = password
self.driver = initialize(baseurl)
super(LoginPage, self).__init__()
def login(self):
driver.find_element_by_id("username").clear()
driver.find_element_by_id("username").send_keys(self.username)
driver.find_element_by_id("password").clear()
driver.find_element_by_id("password").send_keys(self.password)
driver.find_element_by_id("login_button").click()
return HomePage()
class HomePage(URLVerifyingPage):
def some_method(self):
...
return SomePage()
def many_more_methods(self):
...
return ManyMorePages()
It's no big deal if a page gets instantiated a handful of times -- the methods will just get wrapped a handful of times and a handful of unnecessary checks will take place, but everything will still work. But it would be bad if a page was instantiated dozens or hundreds or tens of thousands of times. I could just put a flag in the class definition for each page and check to see if the methods have already been wrapped, but I like the idea of keeping the class definitions pure and clean and shoving all the hocus-pocus into a deep corner of my system where no one can see it and it just works.
In Python, it's almost never worth trying to "force" anything. Whatever you come up with, someone can get around it by monkeypatching your class, copying and editing the source, fooling around with bytecode, etc.
So, just write your factory, and document that as the right way to get an instance of your class, and expect anyone who writes code using your classes to understand TOOWTDI, and not violate it unless she really knows what she's doing and is willing to figure out and deal with the consequences.
If you're just trying to prevent accidents, rather than intentional "misuse", that's a different story. In fact, it's just standard design-by-contract: check the invariant. Of course at this point, SillyBaseClass is already screwed up, and it's too late to repair it, and all you can do is assert, raise, log, or whatever else is appropriate. But that's what you want: it's a logic error in the application, and the only thing to do is get the programmer to fix it, so assert is probably exactly what you want.
So:
class SillyBaseClass:
singletons = {}
class Foo(SillyBaseClass):
def __init__(self):
assert self.__class__ not in SillyBaseClass.singletons
def get_foo():
if Foo not in SillyBaseClass.singletons:
SillyBaseClass.singletons[Foo] = Foo()
return SillyBaseClass.singletons[Foo]
If you really do want to stop things from getting this far, you can check the invariant earlier, in the __new__ method, but unless "SillyBaseClass got screwed up" is equivalent to "launch the nukes", why bother?
it sounds like you want to provide a __new__ implementation: Something like:
class MySingledtonBase(object):
instance_cache = {}
def __new__(cls, arg1, arg2):
if cls in MySingletonBase.instance_cache:
return MySingletonBase.instance_cache[cls]
self = super(MySingletonBase, cls).__new__(arg1, arg2)
MySingletonBase.instance_cache[cls] = self
return self
Rather than adding complex code to catch mistakes at runtime, I'd first try to use convention to guide users of your module to do the right thing on their own.
Give your classes "private" names (prefixed by an underscore), give them names that suggest they shouldn't be instantiated (eg _Internal...) and make your factory function "public".
That is, something like this:
class _InternalSubClassOne(_BaseClass):
...
class _InternalSubClassTwo(_BaseClass):
...
# An example factory function.
def new_object(arg):
return _InternalSubClassOne() if arg == 'one' else _InternalSubClassTwo()
I'd also add docstrings or comments to each class, like "Don't instantiate this class by hand, use the factory method new_object."
You can also just nest classes in factory method, as described here:
https://python-3-patterns-idioms-test.readthedocs.io/en/latest/Factory.html#preventing-direct-creation
Working example from mentioned source:
# Factory/shapefact1/NestedShapeFactory.py
import random
class Shape(object):
types = []
def factory(type):
class Circle(Shape):
def draw(self): print("Circle.draw")
def erase(self): print("Circle.erase")
class Square(Shape):
def draw(self): print("Square.draw")
def erase(self): print("Square.erase")
if type == "Circle": return Circle()
if type == "Square": return Square()
assert 0, "Bad shape creation: " + type
def shapeNameGen(n):
for i in range(n):
yield factory(random.choice(["Circle", "Square"]))
# Circle() # Not defined
for shape in shapeNameGen(7):
shape.draw()
shape.erase()
I'm not fan of this solution, just want to add this as one more option.