Replace Whole Object Through Object Method

S

Stefan Schwarzer

Hi Bruno :)

FWIW, a good part of the canonical design patterns are mostly workaround
the lack of flexibility in languages like C++ and Java.

May be, but that doesn't exclude that some patterns are also
useful in Python. :)
The Strategy
pattern's simplest Python implementation is to dynamically replace a
method on a per-object basis. The State pattern's simplest
implementation in Python is to dynamically change the class of an object.

It may be the "simplest" but it might not be the most "natural"
in terms of the problem domain.

At the Informationsdienst Wissenschaft ( http://idw-online.de )
we had a similar design problem. There, a person can have many
roles, like subscriber, journalist, public info officer etc. We
found it most natural to implement this with the actor/role
pattern, as I V described it.
Of course, one of the canonical solutions to the OP's problem is to use
composition/delegation. But doing it the canonical way would imply
creating instances of the role objects and passing them the instance as
param so they can access it's attributes. It works, but it creates a
cyclic dependancy between the object and it's roles, so you would have
to use weakrefs.

You don't have to, unless both classes have a __del__ method and
the garbage collector thus can't decide which to collect/delete
first.
Dynamically creating new classes with the appropriate
bases and assigning them to the object's __class__ attribute is another
way to achieve the same result, and it's perfectly legal.

It might be "legal" but it also may be confusing. I would use the
design that makes the software easiest to maintain in the long
run. (If you don't know what the long run is, consider the
"medium run" and refactor later. :) )
Now I do agree that it can become tricky to manage correctly wrt/ mro
rules !-)

See? ;-) Software shouldn't be tricky (or only as tricky as
necessary). I value clarity over cleverness.

Stefan
 
J

John Roth

How can an object replace itself using its own method? See the
following code

[code deleted]

At the risk of creating totally incomprehensible and
unmaintainable code, you need two things:

First, you can change the class of any new style
object by assigning to the __class__ attribute.

Second, you can create a new style class
on the fly with the three operand type(a, b, c) method.
See the builtin methods in the doc.

It's quite straightforward if you know what you're
doing. What's hard is to create a conceptual model
that won't be an instant winner in an obfuscated code
contest.

John Roth
 
D

digitalorganics

Fredrik said:
so why not just do that, instead of trying to come up with overly
convoluted ways to abuse a class mechanism that you don't fully understand ?

Why not just do what? And the way you use the term abuse, almost as if
Python's class mechanism was someone's child...
 
D

digitalorganics

Point well taken, and indeed a brilliant solution. Thank you I V for
demonstrating so clearly.

I said:
A misuse of inheritance eh? Inheritance, like other language features,
is merely a tool. I happen to be using this tool to have my virtual
persons change roles at different points in their lifetime, as many
real people tend to do. Thus, at these points, B is indeed an A. What a
person is, whether in real life or in my program, is not static and
comes into definition uniquely for each moment (micro-moment, etc.) of
existence. Now, please, I have no intention of carrying the
conversation in such a silly direction, I wasn't inviting a discussion
on philosophy or some such. I seek to work the tools to my needs, not
the other way around.

But thinking about the problem in the vocabulary provided by the
programming language can be helpful in coming up with a solution. If
inheritance tells you what an object _is_, and membership tells you what a
role _has_, and a role is something that a person has, that suggests
that an implementation where roles are members of a person might be
simpler than trying to use inheritance. Like, for instance:

class Role(object):
def __init__(self, person):
self.person = person


class Worker(Role):
def do_work(self):
print self.name, "is working"


class Employer(Role):

def __init__(self, person):
Role.__init__(self, person)
self.employees = []


def add_employee(self, worker):
self.employees.append(worker)


def boss_people_around(self):
for employee in employees:
print self.name, "is telling", employee.name, "what to do"


class Person(object):

def __init__(self, name):
self.roles = []
self.name = name


def add_role(self, role_class):
self.roles.append(role_class(self))


def forward_to_role(self, attr):
for role in self.roles:
try:
return getattr(role, attr)
except AttributeError:
pass
raise AttributeError(attr)


def __getattr__(self, attr):
self.forward_to_role(attr)


bill = Person('Bill')
bill.add_role(Worker)

bob = Person('Bob')
bob.add_role(Employer)

bill.work()
bob.boss_people_around()
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top