the annoying, verbose self

T

Ton van Vliet

And here lies the problem: The compiler decides at compile time which
names are local to a function and there is no equivalent of a record
definition to make that decision.

The whole clause following 'using self:' could be seen as the record
definition: all attribute assignments/bindings in there should be
prefixed with 'self' (if not already existing in the self namespace?)
and thereby become instance attributes, which outside the 'using self'
clause still need 'self.' as the (namespace?) prefix.

It would not bring an 'implicit' self, but could possibly improve
readability in some areas (btw, personally, I don't have anything
against the 'explicit' self in general)

Even in Pascal the 'with' statement was not meant to improve speed,
but merely for readability, especially with complex records.

Please, bear with me, I am relatively new to Python (reading books,
scanning newsgroups, etc) and feel in no way capable of giving
'educated' or 'well overthought' advise, just my 2 cents ;-)
 
C

Colin J. Williams

Kay said:
Colin said:
I had never thought of trying the above, which is, essentially what I
was
suggesting, as the syntax specifies:

primary ::=
atom | attributeref
| subscription | slicing | call

attributeref ::=
primary "." identifier

I did try it and get:

# tmp.py
class Z():
def __init__(self):
a= 1
self.b= 2
#.c= 3 Marked as a syntax error by PyScripter

def y(self):
self.a= 4
#.b= 5 Marked as a syntax error by PyScripter

It seems that some, probably simple, change in the parsing is needed.

Colin W.

Sure. Since you cite the grammar let me say that I find it somewhat
confusing that the grammar in the Python documentation doesn't
correspond to the grammar used by the CPython parser. For the following
I will use the notations of the Grammar file used by the CPython parser.

In order to adapt the syntax take a look at

atom: ('(' [yield_expr|testlist_gexp] ')' |
'[' [listmaker] ']' |
'{' [dictmaker] '}' |
'`' testlist1 '`' |
NAME | NUMBER | STRING+)

The last row must be changed to

['.'] NAME | NUMBER | STRING+)
~~~~~

But then you run into conflict with the definition of the ellipsis in
rule

subscript: '.' '.' '.' | test | [test] ':' [test] [sliceop]

because you need two token of lookahead. Pythons LL(1) parser might not
manage this.

This problem vanishes with Python 3.0 which defines an own ellipsis
literal:

atom: ('(' [yield_expr|testlist_comp] ')' |
'[' [testlist_comp] ']' |
'{' [dictorsetmaker] '}' |
NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False')

Kay
Many thanks for this clarification. I hope that those beavering away on
Python 3000 will give further consideration to this matter.

Colin W.
 
M

Marc 'BlackJack' Rintsch

I'm not sure what you mean. In the example that I gave, the 'global'
statement isn't needed. However, here's a different example:

I mean that global names that are (re)bound from within functions couple
these functions in a non-obvious way and make the code and data flow harder
to follow and understand. Also it makes refactoring and testing more
difficult because of the dependencies.

Ciao,
Marc 'BlackJack' Rintsch
 
M

Marc 'BlackJack' Rintsch

The whole clause following 'using self:' could be seen as the record
definition: all attribute assignments/bindings in there should be
prefixed with 'self' (if not already existing in the self namespace?)
and thereby become instance attributes, which outside the 'using self'
clause still need 'self.' as the (namespace?) prefix.

So::

def meth(self):
using self:
tmp = raw_input('Enter age: ')
age = int(tmp)

becomes::

def meth(self):
using self:
self.tmp = self.raw_input('Enter age: ')
self.age = self.int(tmp)

Binding `tmp` unnecessarily to the object and trying to get `raw_input()`
and `int()` from the object. Ouch. :)

Ciao,
Marc 'BlackJack' Rintsch
 
T

Ton van Vliet

So::

def meth(self):
using self:
tmp = raw_input('Enter age: ')
age = int(tmp)

becomes::

def meth(self):
using self:
self.tmp = self.raw_input('Enter age: ')
self.age = self.int(tmp)

Binding `tmp` unnecessarily to the object and trying to get `raw_input()`
and `int()` from the object. Ouch. :)

Absolutely.

However, I was more thinking in terms of attributes only, like this:

def meth(self):
using self:
self.tmp = raw_input('Enter age: ')
self.age = int(self.tmp)

and for the methods check whether there *is* a self.<method> defined
in the class (or even within the 'using' clause), then use it, or else
leave it as it is.

It would require more 'lookup' work, but we're talking 'readability'
here (which would btw not bring much in the example you give ;-)

It would boil down to choice: explicit/speed vs implicit/readability
 
D

Duncan Booth

Ton van Vliet said:
It would boil down to choice: explicit/speed vs implicit/readability

No, it would boil down to explicit+speed+readability+maintainability vs
implicit+error prone.

It would mean that as well as the interpreter having to search the
instance to work out whether each name referenced an attribute or a global
the reader would also have to perform the same search. It would mean that
adding a new attribute to an instance would change the meaning of the
methods which is a recipe for disaster.
 
S

samwyse

I mean that global names that are (re)bound from within functions couple
these functions in a non-obvious way and make the code and data flow harder
to follow and understand. Also it makes refactoring and testing more
difficult because of the dependencies.

The whole point of this sub-thread is the difficulty of turning global
vars and functions into class vars and functions, and that is
something that is usually done precisely because the code and data
flow has become harder to follow and understand.
 
M

Marc 'BlackJack' Rintsch

The whole point of this sub-thread is the difficulty of turning global
vars and functions into class vars and functions, and that is
something that is usually done precisely because the code and data
flow has become harder to follow and understand.

Then don't use "global variables". If you don't put anything except
constants, classes and functions in the module's namespace there's no
problem "lifting" them into a class later.

Ciao,
Marc 'BlackJack' Rintsch
 
S

samwyse

No, it would boil down to explicit+speed+readability+maintainability vs
implicit+error prone.

It would mean that as well as the interpreter having to search the
instance to work out whether each name referenced an attribute or a global
the reader would also have to perform the same search. It would mean that
adding a new attribute to an instance would change the meaning of the
methods which is a recipe for disaster.

Besides Pascal, Visual Basic also offers a 'with' statement that
behaves almost in this way. That in itself should be an indication
that the whole thing is a bad idea. ;-)

The way it works, however, is that you do have to prefix members with
a '.' and the interpreter tries to bind with each nested 'with'
variable in turn. It's tolerable with one 'with' statment, but once
you start nesting them it becomes dangerous.

My idea in a parallel thread is to treat a '.' prefix like '../' in
file paths; each one moves you up a level in the symbol table. In a
top-level function, it means a global name; in a class method it means
a member. The @classmethod and @staticmethod decorators would need to
fix things so that '.' refers to the appropriate things. There's no
reason why a 'using' statement couldn't perform nesting as well: '.'
refers to the 'using' variable, '..' refers to what '.' previously
referred to, etc.

OTOH, the point of 'using' is to reduce typing, so you might instead
add 'as' clauses as an alternate way to reduce confusion:
p.do_something()
p.something_else()

Of course, now its starting to look more like a Python 'with'
statement, and I think there's a way to do basically this already.
 
T

Ton van Vliet

No, it would boil down to explicit+speed+readability+maintainability vs
implicit+error prone.

It would not be a full fledged *implicit*, but only used in small
areas where many self's are coming together, and possibly clutter
readability (a subjective classification anyhow) and even could
degrade (code) maintainability.
It would mean that as well as the interpreter having to search the
instance to work out whether each name referenced an attribute or a global
the reader would also have to perform the same search.

IMHO if it is limited to small overviewable sections I think it could
make life of the reader/coder/maintainer even easier and less error
prone.

I cannot judge for the interpreter part (*if* at all doable, it would
probably be the coder's choice between speed and readability)
It would mean that adding a new attribute to an instance would change
the meaning of the methods which is a recipe for disaster.

I don't see what you have in mind here?

As stated some message levels back: 'bear with me', I'm not an expert,
just a beginning user ;-)

I have no problems with the explicit self at all, but just brought up
Pascal's 'with' statement, invented to improve readability only (and
saving some typing as well) and 'transposed' it to python, just as my
2 cents.

I don't want to defend or formally propose an implementation, just
giving comments where *I* do not fully understand the counter
arguments.

So let's not 'beat it to death', ok ;-)
 
P

Patrick Mullen

So::

def meth(self):
using self:
tmp = raw_input('Enter age: ')
age = int(tmp)

becomes::

def meth(self):
using self:
self.tmp = self.raw_input('Enter age: ')
self.age = self.int(tmp)

Binding `tmp` unnecessarily to the object and trying to get `raw_input()`
and `int()` from the object. Ouch. :)

Yes, that's no good. So you would write it like so:

def meth(self,*args):
tmp = int(raw_input('Enter age:'))
using self:
age = tmp

Still an unnecessary lookup on tmp though :) And it would be useless
to use it for one assignment, the idea is to eliminate all the typing
with this:

self.var1 = 5
self.var2 = "a value"
self.var3 = stuff
self.var4 = [2,54,7,7]
self.var5 = "dingaling"
self.var6 = 6.4
self.var7 = 1
self.var8 = False
self.var9 = True

Of course that "self.var3 = stuff" under the using would result in a
bad lookup for "stuff", but the programmer who wanted to use this
would have to realize this and try to avoid it.

I have been known from time to time, for long assignments such as
this, to turn a string with keys and values into a dictionary, and
then update __dict__ with that dictionary, hehe. "var1,5;var2,"a
value";var3,stuff" Not the best practice but the fastest to type.
Sometimes I actually use a dictionary, but typing all of the quotes
for the keys gets old.

If there were a "using" or if the with statement would handle
something like this, I wouldn't use it. "s." is only 2 characters. I
saw chained dots mentioned. Chained dots are 2 characters. Why are
we still discussing this? "s." is the answer, or pulling the
attributes into local vars if you are going to use them many times, to
save lookup. This is not a band-aid, this is an actual valid
programming technique. There is more to programming than typing...



Self is never going away, most python programmers generally like or
aren't bothered by it, if you are new to the language try to get used
to it, if it's too bothersome you can use one of the hacks or try
other languages. I don't mean to be snobby either, one language does
not fit all.
 
P

Paul Boddie

Yes, that's no good. So you would write it like so:

def meth(self,*args):
tmp = int(raw_input('Enter age:'))
using self:
age = tmp

Still an unnecessary lookup on tmp though :)

Indeed. As has been mentioned, it's all about resolving names and how
much of that work gets done at run-time (and whether the magic
confuses the human reader further).
And it would be useless
to use it for one assignment, the idea is to eliminate all the typing
with this:

self.var1 = 5
self.var2 = "a value"
self.var3 = stuff
self.var4 = [2,54,7,7]
self.var5 = "dingaling"
self.var6 = 6.4
self.var7 = 1
self.var8 = False
self.var9 = True

Of course that "self.var3 = stuff" under the using would result in a
bad lookup for "stuff", but the programmer who wanted to use this
would have to realize this and try to avoid it.

I think the remedy is worse than the ailment, especially since the
Pascal "with" construct can make use of declared information about
structure attributes, while the absence of such declarations leaves
more work to be done in Python (and more detective work for future
maintainers of code). Of course, for cases like the above, assuming
that one doesn't feel that lists or dictionaries are acceptable
alternatives to large numbers of instance attributes (which might not
have such regular naming), one might suggest a useful extension of the
attribute access syntax. Taking part of the above example and
rewriting...

self.(var1, var2, var3, var4) = 5, "a value", stuff, [2,54,7,7]

I'm sure Mr Schluehr can provide a working demonstration of this with
very little effort. ;-)

Paul

P.S. There were some proposals for generalisations of the attribute
access mechanisms using not completely different syntax, but I don't
recall them addressing the issue of accessing multiple attributes at
the same time, and the use of arbitrary expressions in place of the
attribute name (where a tuple is used above) gave a distinct feeling
of writing something similar to a combination of the worst aspects of
some shell language with some of the nastier parts of microcomputer
assembly language. Let us, therefore, not get too enthusiastic about
such ideas!
 
G

greg

samwyse said:
so you might instead
add 'as' clauses as an alternate way to reduce confusion:


p.do_something()
p.something_else()

or even

p = myclass.new()
p.do_something()
p.something_else()

Doesn't even need any new syntax. :)
 
G

greg

Patrick said:
Sometimes I actually use a dictionary, but typing all of the quotes
for the keys gets old.

If the keys are all identifiers, you can use keyword
args to the dict constructor. So you could write

self.__dict__.update(dict(var1 = 5,
var2 = "a value", var3 = stuff))

if you really wanted to. (Don't be surprised if
everyone else refuses to maintain your code, though!)
 
G

greg

samwyse said:
Later, I inevitably decide to encapsulate it inside a class, which
means lots of source changes to change my function into a method

You'd be better off changing your design habits to make
things into classes from the beginning if you suspect
you may want it that way later.
 
P

Paul McGuire

For these localized initialization blocks, I don't see anything wrong
with:

_ = self
_.var1 = 5
_.var2 = "a value"
_.var3 = stuff
_.var4 = [2,54,7,7]
_.var5 = "dingaling"
_.var6 = 6.4
_.var7 = 1
_.var8 = False
_.var9 = True

Or if you wanted to simulate something like using or with:

for _ in [self]:
_.var1 = 5
_.var2 = "a value"
_.var3 = stuff
_.var4 = [2,54,7,7]
_.var5 = "dingaling"
_.var6 = 6.4
_.var7 = 1
_.var8 = False
_.var9 = True

-- Paul


-- Paul
 
C

Colin J. Williams

Steven said:
-2 Readability counts, and your example is a lot less readable.

For your example to be even *slightly* readable, you have to fill the
expression with excessive whitespace. A little bit of whitespace is good.
Too much breaks the flow of the expression and hurts readability.

Or perhaps I should say:

T o o m u c h b r e a k s t h e f l o w . . .


You write: math.sqrt(.x * .x + .y * .y + .z * .z)

which to my eyes has too much whitespace, but the alternative is worse:
math.sqrt(.x*.x + .y*.y + .z*.z)

and this is positively painful to try to read, it looks like line-noise:
math.sqrt(.x*.x+.y*.y+.z*.z)



The correct solution to your example is to get rid of the attribute
lookups from the expression completely:


def abs(self):
x, y, z = self.x, self.y, self.z
return math.sqrt(x**2 + y**2 + z**2)


It's probably also faster, because it looks up the attributes only once
each, instead of twice.
Alternatively, as someone else
suggested, an analogue of the Pascal "with"
could be used:

def abs(self):
with self:
return math.sqrt(x**2 + y**2 + z**2)

As has already been pointed out, "with"
has been pre-empted (unfortunately,
in my view) for another purpose.

This form could be generalized to "with
aa" where aa is any object with
attributes accessible with the z= aa.z
or aa.z.= z style.

This should not apply to special names,
such as __add__ etc.

Colin W.
 
C

Colin J. Williams

Steven said:
-2 Readability counts, and your example is a lot less readable.

For your example to be even *slightly* readable, you have to fill the
expression with excessive whitespace. A little bit of whitespace is good.
Too much breaks the flow of the expression and hurts readability.

Or perhaps I should say:

T o o m u c h b r e a k s t h e f l o w . . .


You write: math.sqrt(.x * .x + .y * .y + .z * .z)

which to my eyes has too much whitespace, but the alternative is worse:
math.sqrt(.x*.x + .y*.y + .z*.z)

and this is positively painful to try to read, it looks like line-noise:
math.sqrt(.x*.x+.y*.y+.z*.z)



The correct solution to your example is to get rid of the attribute
lookups from the expression completely:


def abs(self):
x, y, z = self.x, self.y, self.z
return math.sqrt(x**2 + y**2 + z**2)


It's probably also faster, because it looks up the attributes only once
each, instead of twice.
Alternatively, as someone else
suggested, an analogue of the Pascal "with"
could be used:

def abs(self):
with self:
return math.sqrt(x**2 + y**2 + z**2)

As has already been pointed out, "with"
has been pre-empted (unfortunately,
in my view) for another purpose.

This form could be generalized to "with
aa" where aa is any object with
attributes accessible with the z= aa.z
or aa.z.= z style.

This should not apply to special names,
such as __add__ etc.

Colin W.
 
M

MonkeeSage

I like the explicit "self", personally. It helps distinguish class
methods from functions. When I see a "self" I think "A-ha, a class
method". Of course, I could tell that from just the indentation and
following that back to the class declaration, but as a quick reference
I find it helpful. Besides, none of the proposals have sufficinetly
dealt with lexical scope without introducing a lot of costly checks
which would also hide the complexity of the process (and thereby, most
likely, lead to abuse and bad practices).

Regards,
Jordan
 
C

Colin J. Williams

Kay said:
Colin said:
I had never thought of trying the above, which is, essentially what I was
suggesting, as the syntax specifies:

primary ::=
atom | attributeref
| subscription | slicing | call

attributeref ::=
primary "." identifier

I did try it and get:

# tmp.py
class Z():
def __init__(self):
a= 1
self.b= 2
#.c= 3 Marked as a syntax error by PyScripter

def y(self):
self.a= 4
#.b= 5 Marked as a syntax error by PyScripter

It seems that some, probably simple, change in the parsing is needed.

Colin W.

Sure. Since you cite the grammar let me say that I find it somewhat
confusing that the grammar in the Python documentation doesn't
correspond to the grammar used by the CPython parser. For the following
I will use the notations of the Grammar file used by the CPython parser.

In order to adapt the syntax take a look at

atom: ('(' [yield_expr|testlist_gexp] ')' |
'[' [listmaker] ']' |
'{' [dictmaker] '}' |
'`' testlist1 '`' |
NAME | NUMBER | STRING+)

The last row must be changed to

['.'] NAME | NUMBER | STRING+)
~~~~~

But then you run into conflict with the definition of the ellipsis in rule

subscript: '.' '.' '.' | test | [test] ':' [test] [sliceop]

because you need two token of lookahead. Pythons LL(1) parser might not
manage this.

This problem vanishes with Python 3.0 which defines an own ellipsis literal:

atom: ('(' [yield_expr|testlist_comp] ')' |
'[' [testlist_comp] ']' |
'{' [dictorsetmaker] '}' |
NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False')

Kay
Kay,

Could you please elaborate "This problem
vanishes with Python 3.0 which
defines an own ellipsis literal"?

It seems that ellipsis remains "..." -
I've never found a use for it.
See
http://docs.python.org/dev/3.0/library/constants.html#Ellipsis

Colin W.
 

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

Forum statistics

Threads
474,433
Messages
2,571,683
Members
48,796
Latest member
Greg L.

Latest Threads

Top