Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #50277 > unrolled thread
| Started by | Russel Walker <russ.pobox@gmail.com> |
|---|---|
| First post | 2013-07-09 15:01 -0700 |
| Last post | 2013-07-10 17:39 -0600 |
| Articles | 11 — 5 participants |
Back to article view | Back to comp.lang.python
Recursive class | can you modify self directly? Russel Walker <russ.pobox@gmail.com> - 2013-07-09 15:01 -0700
Re: Recursive class | can you modify self directly? Ian Kelly <ian.g.kelly@gmail.com> - 2013-07-09 16:18 -0600
Re: Recursive class | can you modify self directly? Ian Kelly <ian.g.kelly@gmail.com> - 2013-07-09 16:20 -0600
Re: Recursive class | can you modify self directly? Russel Walker <russ.pobox@gmail.com> - 2013-07-10 02:00 -0700
Re: Recursive class | can you modify self directly? Dave Angel <davea@davea.name> - 2013-07-09 18:30 -0400
Re: Recursive class | can you modify self directly? Ethan Furman <ethan@stoneleaf.us> - 2013-07-09 15:19 -0700
Re: Recursive class | can you modify self directly? Russel Walker <russ.pobox@gmail.com> - 2013-07-10 01:58 -0700
Re: Recursive class | can you modify self directly? Terry Reedy <tjreedy@udel.edu> - 2013-07-10 15:33 -0400
Re: Recursive class | can you modify self directly? Russel Walker <russ.pobox@gmail.com> - 2013-07-10 13:09 -0700
Re: Recursive class | can you modify self directly? Russel Walker <russ.pobox@gmail.com> - 2013-07-10 15:50 -0700
Re: Recursive class | can you modify self directly? Ian Kelly <ian.g.kelly@gmail.com> - 2013-07-10 17:39 -0600
| From | Russel Walker <russ.pobox@gmail.com> |
|---|---|
| Date | 2013-07-09 15:01 -0700 |
| Subject | Recursive class | can you modify self directly? |
| Message-ID | <61751485-a298-403e-8b44-be7cf2504f0e@googlegroups.com> |
Sorry for the vague title. Probably best to just show you the code that explains it better.
This is a simplified example of what I want to do:
# THIS DOESN'T WORK
from random import choice
class Expr(object):
"""
Expr(expr, op, val) -> an expression object.
"""
def __init__(self, expr, op='', val=''):
self.expr = expr # can be another instance of Expression.
self.op = op
self.val = val
def __str__(self):
return ("%s %s %s" % (self.expr, self.op, self.val)).strip()
def expand(self):
self = Expr(self, choice('+-*/'), choice('12345'))
Then I tried messing around with Expr.__new__() and Expr.__init__() but that didn't work.
The only way I've got it to work is by doing the expanding part outside of the object, by a method of some other class. But I want the Expr class to be responsible for itself. How can I do this and why doesn't the above work?
[toc] | [next] | [standalone]
| From | Ian Kelly <ian.g.kelly@gmail.com> |
|---|---|
| Date | 2013-07-09 16:18 -0600 |
| Message-ID | <mailman.4475.1373408331.3114.python-list@python.org> |
| In reply to | #50277 |
On Tue, Jul 9, 2013 at 4:01 PM, Russel Walker <russ.pobox@gmail.com> wrote:
> Sorry for the vague title. Probably best to just show you the code that explains it better.
>
> This is a simplified example of what I want to do:
>
>
> # THIS DOESN'T WORK
> from random import choice
>
> class Expr(object):
> """
> Expr(expr, op, val) -> an expression object.
> """
>
> def __init__(self, expr, op='', val=''):
> self.expr = expr # can be another instance of Expression.
> self.op = op
> self.val = val
>
> def __str__(self):
> return ("%s %s %s" % (self.expr, self.op, self.val)).strip()
>
> def expand(self):
> self = Expr(self, choice('+-*/'), choice('12345'))
"self" is just a local binding of the object to the name self. You
can rebind the name like this as with any other local variable, but
that's all it does. It doesn't modify the object in any way, and no
other bindings of the same object are affected.
If you actually want to modify the current object, you would need to
do something like:
def expand(self):
import copy
self.expr = Expr(self.expr, self.op, self.val)
self.op = choice('+-*/')
self.val = choice('12345')
[toc] | [prev] | [next] | [standalone]
| From | Ian Kelly <ian.g.kelly@gmail.com> |
|---|---|
| Date | 2013-07-09 16:20 -0600 |
| Message-ID | <mailman.4476.1373408491.3114.python-list@python.org> |
| In reply to | #50277 |
On Tue, Jul 9, 2013 at 4:18 PM, Ian Kelly <ian.g.kelly@gmail.com> wrote:
> If you actually want to modify the current object, you would need to
> do something like:
>
> def expand(self):
> import copy
> self.expr = Expr(self.expr, self.op, self.val)
> self.op = choice('+-*/')
> self.val = choice('12345')
Minus the "import copy". I modified the code before sending and
forgot to remove that.
[toc] | [prev] | [next] | [standalone]
| From | Russel Walker <russ.pobox@gmail.com> |
|---|---|
| Date | 2013-07-10 02:00 -0700 |
| Message-ID | <db74cbbe-629a-4ddf-868e-70d5967cdb75@googlegroups.com> |
| In reply to | #50279 |
On Wednesday, July 10, 2013 12:20:47 AM UTC+2, Ian wrote:
> On Tue, Jul 9, 2013 at 4:18 PM, Ian Kelly <ian.g.kelly@gmail.com> wrote:
>
> > If you actually want to modify the current object, you would need to
>
> > do something like:
>
> >
>
> > def expand(self):
>
> > import copy
>
> > self.expr = Expr(self.expr, self.op, self.val)
>
> > self.op = choice('+-*/')
>
> > self.val = choice('12345')
>
>
>
> Minus the "import copy". I modified the code before sending and
>
> forgot to remove that.
Yes! This is exactly it. Thanks :)
[toc] | [prev] | [next] | [standalone]
| From | Dave Angel <davea@davea.name> |
|---|---|
| Date | 2013-07-09 18:30 -0400 |
| Message-ID | <mailman.4477.1373409051.3114.python-list@python.org> |
| In reply to | #50277 |
On 07/09/2013 06:01 PM, Russel Walker wrote:
> Sorry for the vague title. Probably best to just show you the code that explains it better.
>
> This is a simplified example of what I want to do:
>
>
> # THIS DOESN'T WORK
> from random import choice
>
> class Expr(object):
> """
> Expr(expr, op, val) -> an expression object.
> """
>
> def __init__(self, expr, op='', val=''):
> self.expr = expr # can be another instance of Expression.
> self.op = op
> self.val = val
>
> def __str__(self):
> return ("%s %s %s" % (self.expr, self.op, self.val)).strip()
>
> def expand(self):
> self = Expr(self, choice('+-*/'), choice('12345'))
>
>
> Then I tried messing around with Expr.__new__() and Expr.__init__() but that didn't work.
>
> The only way I've got it to work is by doing the expanding part outside of the object, by a method of some other class. But I want the Expr class to be responsible for itself. How can I do this and why doesn't the above work?
>
That line in method expand() indicates that you have a fundamental
misunderstanding of the way that names and objects interact in Python.
Names are bound to objects by the assignment operator, but the object
itself is not changed. If you want to change the object that self is
bound to, then change it by mutating it.
You don't change an existing object by binding a name to a new object,
whether of the same or different type. The original object remains
unchanged. The fact that the name is 'self' is totally irrelevant.
a = 45
b = a
a = 12
This does NOT change b.
No idea what you really wanted, but perhaps it's something like:
def expand(self):
self.op = choice("+-*/")
self.val = choice("12345")
No idea what expr is supposed to represent, so I didn't try to
manipulate it here.
--
DaveA
[toc] | [prev] | [next] | [standalone]
| From | Ethan Furman <ethan@stoneleaf.us> |
|---|---|
| Date | 2013-07-09 15:19 -0700 |
| Message-ID | <mailman.4478.1373410388.3114.python-list@python.org> |
| In reply to | #50277 |
On 07/09/2013 03:01 PM, Russel Walker wrote:
>
> This is a simplified example of what I want to do:
>
>
> # THIS DOESN'T WORK
> from random import choice
>
> class Expr(object):
> """
> Expr(expr, op, val) -> an expression object.
> """
>
> def __init__(self, expr, op='', val=''):
> self.expr = expr # can be another instance of Expression.
> self.op = op
> self.val = val
>
> def __str__(self):
> return ("%s %s %s" % (self.expr, self.op, self.val)).strip()
>
> def expand(self):
> self = Expr(self, choice('+-*/'), choice('12345'))
`self` is just a name. In `expand()` you are rebinding the name `self` away from the object and to a new Expr instance.
If you want to change `self` the original object you have to do something like:
def expand(self):
self.op = choice('+-*/')
self.val = choice('12345')
--
~Ethan~
[toc] | [prev] | [next] | [standalone]
| From | Russel Walker <russ.pobox@gmail.com> |
|---|---|
| Date | 2013-07-10 01:58 -0700 |
| Message-ID | <42591572-a09b-4362-8da2-d083c6424b4a@googlegroups.com> |
| In reply to | #50277 |
I didn't do a good job of explaining it cos I didn't want it to be a TLDR; but I could've added a little more. To clarify: Expr is just a way to represent simple arithmetic expressions for a calculator. Because the expression has to be modified and built over time, and evaluated from left to right and not by operator precedence, I thought it would be simpler to do it this way. Say the calculation in the calculator is currently at: 1 + 2 * 3 Now lets expand it out to see how it gets to this point. dial 1: (1) >>> x = Expr(1) # the base case dial +: ((1) + ) >>> x = Expr(x, '+') dial 2: ((1) + 2) >>> x.val = 2 dial *: (((1) + 2) + ) >>> x = Expr(x, '+') dial 3: (((1) + 2) + 3) >>> x.val = 3 I think it's called 'binary tree' but not entirely sure if that's correct. So I think understand what you guys are saying now. There is the name x and the class instance (the object) which exists somewhere in memory that x points to. self is just another name that points to the same object (not self in general but the argument passed to the self parameter when a method is called). However if the code inside the method reassigns self to some other object, it doesn't change the fact that x still refers to the original object. So self is just a local variable (an argument). The name self has no relevance to to the name x other than the fact that they point to the same object. So reassigning self has no effect on x. But modifying the object that self points to, does affect x because it points to the same object. Is this correct? So when you call x.somemethod() it's not passing x as the self argument, it's actually passing the object that x points to as the self argument. And that object has know knowledge of the fact that x points to it, or does it?
[toc] | [prev] | [next] | [standalone]
| From | Terry Reedy <tjreedy@udel.edu> |
|---|---|
| Date | 2013-07-10 15:33 -0400 |
| Message-ID | <mailman.4547.1373484820.3114.python-list@python.org> |
| In reply to | #50320 |
On 7/10/2013 4:58 AM, Russel Walker wrote: > There is the name x and the class instance (the object) which exists > somewhere in memory that x points to. self is just another name that > points to the same object (not self in general but the argument > passed to the self parameter when a method is called). However if the > code inside the method reassigns self to some other object, it > doesn't change the fact that x still refers to the original object. > So self is just a local variable (an argument). Yes, parameters *names* are the initial local names of the function. Calling the first parameter of instancemethods 'self' is a convention, not a requirement. > The name self has no > relevance to to the name x other than the fact that they point to the > same object. So reassigning self has no effect on x. But modifying > the object that self points to, does affect x because it points to > the same object. Is this correct? Yes. Multiple names for one objects are 'aliases'. Being able to modify a object with multiple names in different namespaces is both a boon and bug bait. > So when you call x.somemethod() it's not passing x as the self Python does not pass'x': it is 'call-by-object', not 'call-by-name'. > argument, it's actually passing the object that x points to as the > self argument. And that object has know knowledge of the fact that x > points to it, or does it? Some objects (modules, classes, functions) have definition names (.__name__ attributes) that are used in their representations (as in tracebacks). But they have no knowledge of their namespace names. -- Terry Jan Reedy
[toc] | [prev] | [next] | [standalone]
| From | Russel Walker <russ.pobox@gmail.com> |
|---|---|
| Date | 2013-07-10 13:09 -0700 |
| Message-ID | <c8a25e77-8b06-4dee-ad8d-9c63ca76a91a@googlegroups.com> |
| In reply to | #50390 |
On Wednesday, July 10, 2013 9:33:25 PM UTC+2, Terry Reedy wrote: > On 7/10/2013 4:58 AM, Russel Walker wrote: > > > > > There is the name x and the class instance (the object) which exists > > > somewhere in memory that x points to. self is just another name that > > > points to the same object (not self in general but the argument > > > passed to the self parameter when a method is called). However if the > > > code inside the method reassigns self to some other object, it > > > doesn't change the fact that x still refers to the original object. > > > So self is just a local variable (an argument). > > > > Yes, parameters *names* are the initial local names of the function. > > Calling the first parameter of instancemethods 'self' is a convention, > > not a requirement. > > > > > The name self has no > > > relevance to to the name x other than the fact that they point to the > > > same object. So reassigning self has no effect on x. But modifying > > > the object that self points to, does affect x because it points to > > > the same object. Is this correct? > > > > Yes. Multiple names for one objects are 'aliases'. Being able to modify > > a object with multiple names in different namespaces is both a boon and > > bug bait. > > > > > So when you call x.somemethod() it's not passing x as the self > > > > Python does not pass'x': it is 'call-by-object', not 'call-by-name'. > > > > > argument, it's actually passing the object that x points to as the > > > self argument. And that object has know knowledge of the fact that x > > > points to it, or does it? > > > > Some objects (modules, classes, functions) have definition names > > (.__name__ attributes) that are used in their representations (as in > > tracebacks). But they have no knowledge of their namespace names. > > > > > > > > -- > > Terry Jan Reedy Thanks Terry, I did read all that twice just to make sure it sunk in and it was really clear and helpful.
[toc] | [prev] | [next] | [standalone]
| From | Russel Walker <russ.pobox@gmail.com> |
|---|---|
| Date | 2013-07-10 15:50 -0700 |
| Message-ID | <ba392048-0140-4781-9079-f85531ea41de@googlegroups.com> |
| In reply to | #50277 |
I've been mucking around with this silly class pretty much the whole day and my eyes are about closing now so this is the solution for now I think. Please feel free to drop any suggestions. I think I mostly just ended up shaving off allot of extraneous responsibility for the class, that and inheriting from list. I wanted to try it and it seems to fit (I think).
Maybe tomorrow I'll try it inheriting from instead since that is immutable. Should've probably thought of that before right this moment, as I had quite a time with "maximum recursion depth exceeded".
class LRExpression(list):
"""
Construct for 'left to right' algebraic expressions.
"""
def __init__(self, *args):
list.__init__(self, args[:1])
for x in args[1:]:
self.append(x)
def __str__(self):
return '(%s)' % ' '.join(map(str, self))
def evaluate(self):
return eval(str(self))
def append(self, x):
if len(self) < 3:
list.append(self, x)
else:
oldself = LRExpression(*self)
self.__init__(oldself)
self.append(x)
def test():
a = LRExpression(1)
a.append('+')
a.append(2)
a.append('*')
a.append(3)
try:
print 'repr:', repr(a)
print 'str:', a
print "should be 9:", a.evaluate()
except Exception as er:
print er
[toc] | [prev] | [next] | [standalone]
| From | Ian Kelly <ian.g.kelly@gmail.com> |
|---|---|
| Date | 2013-07-10 17:39 -0600 |
| Message-ID | <mailman.4554.1373499593.3114.python-list@python.org> |
| In reply to | #50396 |
On Wed, Jul 10, 2013 at 4:50 PM, Russel Walker <russ.pobox@gmail.com> wrote:
> def append(self, x):
> if len(self) < 3:
> list.append(self, x)
> else:
> oldself = LRExpression(*self)
> self.__init__(oldself)
> self.append(x)
It's probably not wise to be re-calling __init__ after the class has
been initialized, although it seems to work. I would instead use
slice assignment to replace the list contents:
self[:] = [oldself]
That said, the problem with inheriting from list is that it allows
your class to be used in a lot of ways lists can be used that are
probably not appropriate. For example, continuing on from your test
function:
>>> len(a)
3
Why 3? Because that's the length of the list. But the caller would
probably expect the length to have something to do with the size of
the expression, not the list.
>>> '*' in a # Is the '*' operator used in a?
True
>>> '+' in a # What about the '+' operator?
False
Again, this happens because the "in" operator is only looking at the
list itself, not at sublists. But an expression container would
probably be expected to return True for both of those. These cases
can of course be easily overridden in the subclass, but what about
something harder, like slicing? How should that be expected to work
with an LRExpression?
This isn't to say that you shouldn't necessarily use a list if it
helps you implement the behavior you want, but having the LRExpression
*be* a list is probably wrong.
Finally, based on what you're doing here, I think you would be better
off using something like the OOP Builder pattern. There will be two
distinct classes: the "LRExpression" class that composes a tree
structure, and an "ExpressionBuilder" class that does the work of
actual assembly. It could look something like this:
class LRExpression(object):
def __init__(self, expr, op, value):
self.expr = expr
self.op = op
self.value = value
def __str__(self):
return '(%s %s %s)' % (self.expr, self.op, self.value)
def evaluate(self):
# Subject to the usual warning that eval() should
# never be used with untrusted input
return eval(str(self))
class ExpressionBuilder(object):
def __init__(self):
self.parts = []
def append(self, part):
self.parts.append(part)
def build(self):
expr = self.parts[0]
for i in xrange(1, len(self.parts), 2):
op, value = self.parts[i:i+2]
expr = LRExpression(expr, op, value)
return expr
def test():
a = ExpressionBuilder()
a.append(1)
a.append('+')
a.append(2)
a.append('*')
a.append(3)
expr = a.build()
print expr
print expr.evaluate()
>>> test()
((1 + 2) * 3)
9
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web