the annoying, verbose self

B

BJörn Lindqvist

I don't see this as a big deal, but

The big deal is that "self." occupies important horizontal screen real
estate. That is, it is usually not self in itself that is problematic,
but the overflowing lines is. Take this silly vector class for
example:

1 class Vector:
2 def __init__(self, x, y, z):
3 self.x = x
4 self.y = y
5 self.z = z
6 def abs(self):
7 return math.sqrt(self.x * self.x + self.y * self.y +
self.z * self.z)

Line 7 is 82 characters long which is, if you care about the
readability of your code, a very real problem because the line is to
long. So you break it:

7 return math.sqrt(self.x * self.x +
self.y * self.y +
self.z * self.z)

Better, but definitely not as nice as it would have been with a
shorter self prefix like you propose. And in my eyes, having to break
lines like this instantly makes the code much less attractive. There
is probably not a single language in existance in which wrapping lines
doesn't make the code uglier.

I also notice that the lines in your mail are nicely broken at about
column 45, probably because you are typing on a PDA or similar? In
those situations, where you require much shorter lines than the
standard 78 characters, for example when typesetting code examples for
a book, the problem with line breaks is even worse.
suppose that the syntax were
expanded so that, in a method, a dot
".", as a precursor to an identifier,
was treated as "self." is currently treated?

I like that a lot. This saves 12 characters for the original example
and removes the need to wrap it.

7 return math.sqrt(.x * .x + .y * .y + .z * .z)

+1 Readability counts, even on small screens.
 
B

braver

The big deal is that "self." occupies important horizontal screen real
estate. That is, it is usually not self in itself that is problematic,

Exactly. I understand and appreciate all the scoping qualification
and explicit"ation" needs, but I still want my code to look good and
uncluttered after all is said and done. Using indentation saves us
from all those end's or {}, no less annoying than self, -- then the
self comes along and defeats the purpose of conciseness.
but the overflowing lines is. Take this silly vector class for

I like that a lot. This saves 12 characters for the original example
and removes the need to wrap it.

7 return math.sqrt(.x * .x + .y * .y + .z * .z)

+1 Readability counts, even on small screens.

Hear, hear! Dear G-ds of Python, pls give us a --dotself option!

Cheers,
Alexy
 
S

Steven D'Aprano

I like that a lot. This saves 12 characters for the original example and
removes the need to wrap it.

7 return math.sqrt(.x * .x + .y * .y + .z * .z)

+1 Readability counts, even on small screens.

-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.
 
B

BJörn Lindqvist

I like that a lot. This saves 12 characters for the original example and
removes the need to wrap it.

7 return math.sqrt(.x * .x + .y * .y + .z * .z)

+1 Readability counts, even on small screens.
[gibberish]
which to my eyes has too much whitespace, but the alternative is worse:
math.sqrt(.x*.x + .y*.y + .z*.z)

This is merely your personal preference and as such, it is not
possible to argue or put in absolute terms.
and this is positively painful to try to read, it looks like line-noise:
math.sqrt(.x*.x+.y*.y+.z*.z)

At least it is 12 characters less line-noise than
math.sqrt(self.x*self.y+self.y*self.y+self.z*self.z).
The correct solution to your example is to get rid of the attribute
lookups from the expression completely:

No it is not. The "solution" is nothing more than a silly band-aid
thrown out by people who cannot comprehend that Python may not be
perfect in every little detail.
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.

timeit
 
J

John Machin

On Nov 24, 10:54 am, Steven D'Aprano <st...@REMOVE-THIS-
cybersource.com.au> wrote:

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

"correct" in what sense?
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.

Faster still is possible, without any cruft:
.... x, y, z = self.x, self.y, self.z
.... return math.sqrt(x**2 + y**2 + z**2)
........ return math.sqrt(self.x**2 + self.y**2 + self.z**2)
.... 2 0 LOAD_FAST 0 (self)
3 LOAD_ATTR 0 (x)
6 LOAD_FAST 0 (self)
9 LOAD_ATTR 1 (y)
12 LOAD_FAST 0 (self)
15 LOAD_ATTR 2 (z)
18 ROT_THREE
19 ROT_TWO
20 STORE_FAST 1 (x)
23 STORE_FAST 2 (y)
26 STORE_FAST 3 (z)

3 29 LOAD_GLOBAL 3 (math)
32 LOAD_ATTR 4 (sqrt)
35 LOAD_FAST 1 (x)
38 LOAD_CONST 1 (2)
41 BINARY_POWER
42 LOAD_FAST 2 (y)
45 LOAD_CONST 1 (2)
48 BINARY_POWER
49 BINARY_ADD
50 LOAD_FAST 3 (z)
53 LOAD_CONST 1 (2)
56 BINARY_POWER
57 BINARY_ADD
58 CALL_FUNCTION 1
61 RETURN_VALUE 2 0 LOAD_GLOBAL 0 (math)
3 LOAD_ATTR 1 (sqrt)
6 LOAD_FAST 0 (self)
9 LOAD_ATTR 2 (x)
12 LOAD_CONST 1 (2)
15 BINARY_POWER
16 LOAD_FAST 0 (self)
19 LOAD_ATTR 3 (y)
22 LOAD_CONST 1 (2)
25 BINARY_POWER
26 BINARY_ADD
27 LOAD_FAST 0 (self)
30 LOAD_ATTR 4 (z)
33 LOAD_CONST 1 (2)
36 BINARY_POWER
37 BINARY_ADD
38 CALL_FUNCTION 1
41 RETURN_VALUE
HTH,
John
 
N

Neil Cerutti

The big deal is that "self." occupies important horizontal
screen real estate. That is, it is usually not self in itself
that is problematic, but the overflowing lines is. Take this
silly vector class for example:

1 class Vector:
2 def __init__(self, x, y, z):
3 self.x = x
4 self.y = y
5 self.z = z
6 def abs(self):
7 return math.sqrt(self.x * self.x + self.y * self.y +
self.z * self.z)

I like that a lot. This saves 12 characters for the original
example and removes the need to wrap it.

7 return math.sqrt(.x * .x + .y * .y + .z * .z)

+1 Readability counts, even on small screens.

-1 Refactoring is a more expedient response than language
redesign.

def sum_of_squares(*args):
return sum(arg*args for arg in args)
 
H

hdante

Hi,

Python uses "self" (and textual notation when possible) because its
designers consider that symbols reduce readability. Self won't go
away. :p

The issue is related to the first and seventh lines in The Zen of
Python,

1. "Beautiful is better than ugly"
7. "Readability counts."

My opinion is that "self" is a minor issue in python that we can all
live with.
 
P

Patrick Mullen

Most of the time self doesn't bother me in the slightest. The one
time it does bother me however, is when I am turning a function into a
method. In this case, often I have many local variables which I
actually want to be instance variables, so I have to add self to all
of them. Of course, this is going to cause me some grief no matter
which language I am using. If it was enough trouble, it wouldn't be
hard to make a filter that converts my code automatically.

In other cases it's not a big deal. When I program in java I write
"this." on everything anyway, as my Java teacher recommended. So for
me it's not different than java, the way I write it.

I don't particularly like too much use of special characters, such as
the @ in ruby. I actually don't like the @ in python for decorators
either. So ugly :( I would not like to see a special character or
leading dot used for attributes for this reason. Human readable text
is nice, because even if you haven't programmed in the language for a
while, you don't have to remember as many special symbols.

I think using s. is your best bet, but for me, s is then just another
special character that needs to be memorized, whereas self. is self
descriptive.

One persons trash is another person's treasure.
 
G

greg

BJörn Lindqvist said:
6 def abs(self):
7 return math.sqrt(self.x * self.x + self.y * self.y +
self.z * self.z)

I would write that as

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

Not only is it easier to read, it's also more efficient,
because it avoids looking up instance variables more than
once.
 
G

George Sakkis

On Nov 23, 2007 11:54 PM, Steven D'Aprano

No it is not. The "solution" is nothing more than a silly band-aid
thrown out by people who cannot comprehend that Python may not be
perfect in every little detail.

It sure isn't, but what would the best alternative be in this case?
Ruby's "@"? That's only one character less than "s." if "self." is too
long (as a compromise I sometimes use "my."). Also, static languages
can get away with implicit self (or "this") because (1) all attributes
are statically defined in the class body and (2) local variables must
be declared before used; neither of these is true for Python. I
certainly wouldn't consider better a solution that required declaring
local variables (e.g. "local x = 0").

George
 
K

Kay Schluehr

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.

I like this pattern but less much I like the boilerplate. What about
an explicit unpacking protocol and appropriate syntax?

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

expands to

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

This would have another nice effect on expression oriented programming
where people use to create small "domain specific languages" using
operator overloading:

x,y,z by Expr

class Expr(object):
def __init__(self, name):
self.name = name
self._sexpr = ()

def __unpack__(self, *names):
if len(names)>1:
return [Expr(name) for name in names]
elif len(names) == 1:
return Expr(names[0])
else:
raise ValueError("Nothing to unpack")

def __add__(self, other):
e = Expr("")
e._sub = ('+',(self, other))
return e

def __repr__(self):
if self._sexpr:
return self._sexpr[0].join(str(x) for x in self._sexpr[1])
return self.name
4+x
 
M

Marc 'BlackJack' Rintsch

I like this pattern but less much I like the boilerplate. What about
an explicit unpacking protocol and appropriate syntax?

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

expands to

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

What about ``from`` instead of ``by``? That's already a keyword so we
don't have to add a new one.

Ciao,
Marc 'BlackJack' Rintsch
 
K

Kay Schluehr

What about ``from`` instead of ``by``? That's already a keyword so we
don't have to add a new one.

Ciao,
Marc 'BlackJack' Rintsch

I'm rather passionless about keyword selection. I selected a new one
because I didn't want to discuss possible syntactical conflicts or
confusions that might arise by reusing an existing keyword. I think
'from' is fine when it works for the majority of those who are
interested in the proposed statement semantics.

Ciao, Kay
 
T

Ton van Vliet

Most of the time self doesn't bother me in the slightest. The one
time it does bother me however, is when I am turning a function into a
method. In this case, often I have many local variables which I
actually want to be instance variables, so I have to add self to all
of them. Of course, this is going to cause me some grief no matter
which language I am using. If it was enough trouble, it wouldn't be
hard to make a filter that converts my code automatically.

Just bringing up something I sometimes miss from good-old Turbo-Pascal
here, which has the WITH statement to reduce the typing overhead with
(long) record/struct prefixes, used like:

with <prefix> do begin
a = ...
b = ...
end;

where all variables would be automatically prefixed with the <prefix>
(if present in the already available record definition!)

In your case where you have something like:

def somefunction(...):
a = ...
b = ...
...
c = a + b

could (simply :) become something like:

def somefunction(self, ...):
using self:
a = ...
b = ...
...
c = a + b

so only one line extra and an indentation shift.

Of course the interpreter would have to be (much) smarter here, since
the <prefix> (in this case just a simple 'self', but it could be more
complex, e.g. some module.levels.deep) is not necessarily defined in
advance.

Since 'with' is already in use, another keyword (e.g. 'using') would
be needed

I guess speed would also be a major issue, but readibility would gain
from it (IHMO :)
 
M

Marc 'BlackJack' Rintsch

Just bringing up something I sometimes miss from good-old Turbo-Pascal
here, which has the WITH statement to reduce the typing overhead with
(long) record/struct prefixes, used like:

with <prefix> do begin
a = ...
b = ...
end;

where all variables would be automatically prefixed with the <prefix>
(if present in the already available record definition!)

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.

Ciao,
Marc 'BlackJack' Rintsch
 
P

Patrick Mullen

Ton Van Vliet:
[... using/with ...]

This looks like a really nice little construct, and solves my small
quirk issue (which has popped up maybe twice in my python experience).
It could also be a solution to the OP's problem. The issue of course
is disambiguation. Is EVERY name looked up in the tagged object, or
only assignments? Does it copy attributes over and make everything
local and then copy it back at the end (like you would kind of expect
in a function)? Or is everything routed to the parent. It gets very
sticky very fast, but since it's in a block, the issues aren't as evil
as when they show up if you try to get rid of self altogether. It
also solves more than just the self issue, allowing you to more
quickly assign multiple attributes of an object as well.

In your example:

def somefunction(self, ...):
using self:
a = ...
b = ...
...
c = a + b

The biggest issue is, what if you need to do more:

def somefunction(self, ...):
using self:
a = ...
b = ...
c = a + int(b)

What is int? Is it self.int? I suppose the simplest solution is to:
always assign as an attribute, and lookup as an attribute first going
to normal lookup rules after that.

As far as using [xxx], I don't think the interpreter needs to deal
with [xxx] until it actually executes the line. [xxx] would just be a
normal python expression, the result of which is used for the
attribute lookup and assignments within the block.

This would somewhat solve the toy vector example:

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

Unfortunately the performance would be slightly worse do to the extra
lookup rules (self.math? no, self.__getattr__(math)? no,
self.parentclass.__getattr__(math)? no, etc)

The OP will probably say, "why not build the [using self] block into a
method definition":

def abs():
return math.sqrt(x*x .....

But this would cause problems and slowdown for pretty much all python
code in existence, and reopens the whole discussion of why removing
self is bad :)

Anyway, I'm for the "using" construct, as unlikely as it is. It's not
necessary, but something similar could help alleviate a bit of the
tedium that is perceived by some python users, without affecting
anyone else.

Of course, this is all theory, and it's unknown (by me) if the python
compiler can even handle such a thing. Even if it could, it's
probably not an easy task.
 
S

samwyse

Most of the time self doesn't bother me in the slightest. The one
time it does bother me however, is when I am turning a function into a
method. In this case, often I have many local variables which I
actually want to be instance variables, so I have to add self to all
of them. Of course, this is going to cause me some grief no matter
which language I am using. If it was enough trouble, it wouldn't be
hard to make a filter that converts my code automatically.

I've had the same thought, along with another. You see, on of my pet
peeves about all OO languages that that when creating new code, I
generally begin by writing something like this:

cat = 'felix'
dog = 'rover'
def example():
global cat, dog # not always required, but frequently needed
return ', '.join((cat, dog))

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

class my_pets:
cat = 'felix'
dog = 'rover'
def example(self):
return ', '.join((self.cat, self.dog))

My idea is to introduce syntax that treats top-level functions as
methods of a global, anonymous, class:

cat = 'felix'
dog = 'rover'
def example():
return ', '.join((.cat, .dog))

(btw, we'll also stop auto-magically recognizing global variables
inside functions, forcing the use of the '.' in global names.) Thus,
to convert the above into a class, we just need to add one line and
alter the indentation:

class my_pets:
cat = 'felix'
dog = 'rover'
def example(): # anonymous 'self' is implicit inside class defs.
return ', '.join((.cat, .dog))

You'd need someway to refer to higher lexical levels, possibly by
chaining periods; this would break ellipsis but that could be replaced
by a keyword such as 'to'. And you'd probably want to keep 'global',
but as a keyword for those rare cases where you need to force a
reference to the outer-most lexical level:

warming = 42
def generator(factiod):
def decorator(func):
def wrapper(*args, **kwds):
if ..factoid == None:
return .func(*args, **kwds)
else:
return ..factoid + global.warming
return wrapper
return decorator
 
M

Marc 'BlackJack' Rintsch

I've had the same thought, along with another. You see, on of my pet
peeves about all OO languages that that when creating new code, I
generally begin by writing something like this:

cat = 'felix'
dog = 'rover'
def example():
global cat, dog # not always required, but frequently needed
return ', '.join((cat, dog))

Ouch that's bad design IMHO. The need to use ``global`` is a design
smell, if needed *frequently* it starts to stink.

Ciao,
Marc 'BlackJack' Rintsch
 
S

samwyse

Ouch that's bad design IMHO. The need to use ``global`` is a design
smell, if needed *frequently* it starts to stink.

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:
if my_score > top_score:
print "A new high score!"
top_score = myscore
print "Top score:", top_score

Traceback (most recent call last):
File "<pyshell#49>", line 1, in <module>
leaderboard(7, 'samwyse')
File "<pyshell#47>", line 2, in leaderboard
if my_score > top_score:
UnboundLocalError: local variable 'top_score' referenced before
assignment
 
J

jakub silar

BJörn Lindqvist said:
The big deal is that "self." occupies important horizontal screen real
estate. That is, it is usually not self in itself that is problematic,
but the overflowing lines is. Take this silly vector class for
example:

1 class Vector:
2 def __init__(self, x, y, z):
3 self.x = x
4 self.y = y
5 self.z = z
6 def abs(self):
7 return math.sqrt(self.x * self.x + self.y * self.y +
self.z * self.z)

Line 7 is 82 characters long which is, if you care about the
readability of your code, a very real problem because the line is to
long. So you break it:

7 return math.sqrt(self.x * self.x +
self.y * self.y +
self.z * self.z)

Better, but definitely not as nice as it would have been with a
shorter self prefix like you propose. And in my eyes, having to break
lines like this instantly makes the code much less attractive. There
is probably not a single language in existance in which wrapping lines
doesn't make the code uglier.

I also notice that the lines in your mail are nicely broken at about
column 45, probably because you are typing on a PDA or similar? In
those situations, where you require much shorter lines than the
standard 78 characters, for example when typesetting code examples for
a book, the problem with line breaks is even worse.




I like that a lot. This saves 12 characters for the original example
and removes the need to wrap it.

7 return math.sqrt(.x * .x + .y * .y + .z * .z)

+1 Readability counts, even on small screens.
Below is my coding standard - I'm lazy, even lazy to persuade
comutinties into strange (imho) language syntax extensions.


class Vector:
def __init__(s, x, y, z):
s.x = x
s.y = y
s.z = z
def abs(s):
return math.sqrt(s.x * s.x + s.y * s.y + s.z * s.z)

Admit that changing habits may be more difficult then to change a
language syntax.

Jakub

occasional lamerish Python user
 

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,780
Messages
2,569,611
Members
45,280
Latest member
BGBBrock56

Latest Threads

Top