creating class objects inside methods

H

horos11

All,

I've got a strange one..

I'm trying to create a class object inside another class object by
using the code template below (note.. this isn't the exact code.. I'm
having difficulty reproducing it without posting the whole thing)

Anyways, the upshot is that the first time the Myclass() constructor
is called, the __init__ function gets executed.. The second time, it
calls the '__call__' method (and dies, because I don't have a call
method defined).

To get around this, I've made __call__ a synonym of __init__, which is
a horrid hack, and doesn't help me much (since I don't have a good
idea what's going on).

So - anyone have an idea of what's going on here? It looks like the
second time, the Myclass() call is interpreted as a class instance,
not a class object, but that seems odd to me.. Is this a python bug?
I'm seeing it in 2.6..If necessary, I can post the whole piece of
code..

Ed

class Myclass:

def __init__(self, b='default1', c='default2'):

self.b = b;
self.c = c;

def function(self):

other = Myclass();
return(other)

a = Myclass();

b = a.function()
 
S

Simon Forman

All,

I've got a strange one..

I'm trying to create a class object inside another class object by
using the code template below (note.. this isn't the exact code.. I'm
having difficulty reproducing it without posting the whole thing)

Anyways, the upshot is that the first time the Myclass() constructor
is called, the __init__ function gets executed.. The second time, it
calls the '__call__' method (and dies, because I don't have a call
method defined).

To get around this, I've made __call__ a synonym of __init__, which is
a horrid hack, and doesn't help me much (since I don't have a good
idea what's going on).

So - anyone have an idea of what's going on here? It looks like the
second time, the Myclass() call is interpreted as a class instance,
not a  class object, but that seems odd to me.. Is this a python bug?
I'm seeing it in 2.6..If necessary, I can post the whole piece of
code..

Ed

class Myclass:

   def __init__(self, b='default1', c='default2'):

       self.b = b;
       self.c = c;

   def function(self):

        other = Myclass();
        return(other)

a = Myclass();

b = a.function()


class Myclass:
def __init__(self, b='default1', c='default2'):
self.b = b
self.c = c

def function(self):
other = Myclass()
return other

a = Myclass()
b = a.function()

<__main__.Myclass instance at 0x95cd5ac>


What's the problem?


(Also, you can leave out the ';' at the end of statements. And
'return' isn't a function, you can leave out the ()'s.
 
H

horos11

<__main__.Myclass instance at 0x95cd3ec>>>> b

<__main__.Myclass instance at 0x95cd5ac>

What's the problem?

Like I said, the code was a sample of what I was trying to do, not the
entire thing.. I just wanted to see if the metaphor was kosher.

It sounds to me from your answer that this is unexpected behavior, so
I'll go ahead and post the whole thing. My guess is that it is a
python bug..

Run it (a simple puzzle game solved by breadth first search), and the
first time state() is called inside the method, it calls __init__.
Second time, and therafter, it calls __call__. I've highlighted where
the code fails by putting a pdb.set_trace().

Anyways, I've got a workaround (simply pass in any new objects needed
from the caller), but it is truly annoying that python is either
misleading or broken in this way.

Attached find code, does not work vs. 2.6..


Ed

----

from collections import deque
import copy
import pdb

class state:

def default_board():

return [
[ 1, 'x', 'x', 0 ],
[ 2, 2, 3, 4 ],
[ 5, 6, 6, 7 ],
[ 5, 6, 6, 7 ],
[ 8, 9, 10, 10 ],
[ 0, 'x', 'x', 0 ]
]

def default_types():

return {
1 : [ 0, 0 ],
2 : [ 0, 0, 0, 1 ],
3 : [ 0, 0 ],
4 : [ 0, 0 ],
5 : [ 0, 0, 1, 0 ],
6 : [ 0, 0, 1, 0, 0, 1, 1, 1 ],
7 : [ 0, 0, 1, 0 ],
8 : [ 0, 0 ],
9 : [ 0, 0 ],
10 : [ 0, 0, 0, 1 ]
}

def default_moves():

return []

def print_move(self, moveno, move):
print str(moveno) + ": " + str(move) + "\n"


def __init__(self, _board=default_board(), _moves=default_moves(),
_types=default_types()):

self.board = _board
self.moves = _moves
self.types = _types

def possible_moves(self):

moves_so_far = set()
moves_so_far.add('x')
moves_so_far.add(0)
ret = []
for y_idx in range(0, len(self.board)):
for x_idx in range(0, len(self.board[y_idx])):

piece = self.board[y_idx][x_idx]

if not piece in moves_so_far:

moves = self.legal_moves(y_idx, x_idx)
moves_so_far.add(piece)

if moves:
ret.extend(moves)

return ret

def is_answer(self):

if self.board[5][3] == 1:
return True
else:
return False

def legal_moves(self, ycoord, xcoord):

ret = []
for dir in [ [ 0, 1 ], [ 0, -1 ], [ 1, 0 ], [ -1, 0 ] ]:
ret.extend(self.addmove(dir[0], dir[1], ycoord, xcoord))

return ret

def empty(self, type, ycoord, xcoord, pieceno):

for itr in range(0, len(type), 2):

yy = type[itr]
xx = type[itr+1]

if not (len(self.board) > (yy+ycoord) >= 0) or not (len
(self.board[yy+ycoord]) > xx+xcoord >= 0):
return False

if not self.board[yy+ycoord][xx+xcoord] in [ 0, pieceno ]:
return False

return True

def addmove(self, ymult, xmult, ycoord, xcoord):

ret = []
pieceno = self.board[ycoord][xcoord]
type = self.types[pieceno]

if xmult != 0:
for xx in range(xcoord + xmult, -1 if xmult < 0 else 4, -1
if xmult < 0 else 1):
# if xx == 0:
# continue
if self.empty(type, ycoord, xx, pieceno):
ret.append(self.newmove(ycoord, xcoord, ycoord,
xx ))
else:
break

if ymult != 0:
for yy in range(ycoord + ymult, -1 if ymult < 0 else 6, -1
if ymult < 0 else 1):
# if yy == 0:
# continue
if self.empty(type, yy, xcoord, pieceno):
ret.append(self.newmove(ycoord, xcoord, yy,
xcoord))
else:
break

return ret

def newmove(self, fromy, fromx, toy, tox):

move = {
'fromx' : fromx,
'fromy' : fromy,
'toy' : toy,
'tox' : tox,
'piece' : self.board[fromy][fromx]
}

return move

def printout_path(self):

# print self
pdb.set_trace()
answer = state()

moveno = 0
print "\n==========================\n"

for moveno in range(0, len(self.moves)):
move = self.moves[moveno]
self.print_move(moveno, move)
answer.apply_move(move)
answer.print_board()
print "\n==========================\n"

def print_board(self):

for xx in self.board:

print ": ".join([ "%2s" % str(ii) for ii in xx ])

def to_string(self):

return str(self.board)

def apply_move(self, move):

self.moves.append(move)
num = self.board[move['fromy']][move['fromx']]
type = self.types[num]

for idx in range(0,len(type),2):
yy_delta = type[idx]
xx_delta = type[idx+1]
self.board[move['fromy']+ yy_delta][move['fromx']+
xx_delta] = 0

for idx in range(0,len(type),2):
yy_delta = type[idx]
xx_delta = type[idx+1]
self.board[move['toy']+ yy_delta][move['tox']+ xx_delta] =
num

def next_states(self):

ret = []
for move in self.possible_moves():
newstate = copy.deepcopy(self)
newstate.apply_move(move)
ret.append(newstate)

# pdb.set_trace()
# for xx in ret:
# print xx.__dict__
# print "\n"

return ret

seen_states = set()

start = state()
dq = deque()

dq.append(start)


while len(dq):

curstate = dq.popleft()
print "HERE " + str(curstate) + " => " + curstate.to_string() +
"\n"
curstate.printout_path()

if curstate.is_answer():
curstate.printout_path()

elif curstate.to_string() in seen_states:
print "DUP " + curstate.to_string()

else:
seen_states.add(curstate.to_string())
for state in curstate.next_states():
if not state.to_string() in seen_states:
dq.append(state)

print "Trying..\n"
state.print_board()
print "\n"
 
H

horos11

Anyways, I see what's going on here:

With the line,

for state in curstate.next_states():
if not state.to_string() in seen_states:
dq.append(state)

Inadvertently using the name of a module as a variable seems to be
causing this.

In any case, this shouldn't cause issues with constructors, so I'd
call this a bug..

Ed
 
C

Carl Banks

Like I said, the code was a sample of what I was trying to do, not the
entire thing.. I just wanted to see if the metaphor was kosher.

It sounds to me from your answer that this is unexpected behavior, so
I'll go ahead and post the whole thing. My guess is that it is a
python bug..
Sure.


Run it (a simple puzzle game solved by breadth first search), and the
first time state() is called inside the method, it calls __init__.
Second time, and therafter, it calls __call__. I've highlighted where
the code fails by putting a pdb.set_trace().

Anyways, I've got a workaround (simply pass in any new objects needed
from the caller), but it is truly annoying that python is either
misleading or broken in this way.

Attached find code, does not work vs. 2.6..
[snip]

I don't believe you couldn't reduce that to something simpler.

Anyway, I didn't bother reading all the code or running it, but I had
a suspicion what is was so I looked for it and saw what I was looking
for.

Here's you class statement:

class state:


Now here's some code that appears much later at the top level:

    else:
        seen_states.add(curstate.to_string())
        for state in curstate.next_states():
            if not state.to_string() in seen_states:
                dq.append(state)

                print "Trying..\n"
                state.print_board()
                print "\n"


Whoops. It looks like you rebound the global variable "state" to a
new value, which makes the class no longer accessible by that name.

My immediate suggestion is to change the name of the class, and to
capitalize the first letter. "state" is a terrible name for a class.
Class instances, almost by default, maintain state, so naming a class
"state" says almost nothing about what the class is. Something like
GameState or PlayerState would be better.

Here is some reading material that can help you adjust your coding
style to help avoid such errors in the future:

http://www.python.org/dev/peps/pep-0008


Carl Banks
 
C

Carl Banks

Anyways, I see what's going on here:

With the line,

for state in curstate.next_states():
    if not state.to_string() in seen_states:
        dq.append(state)

Inadvertently using the name of a module as a variable seems to be
causing this.

Nope, unless by "module" you meant "class".

In any case, this shouldn't cause issues with constructors, so I'd
call this a bug..

It's not a bug. In Python classes and global variables share the same
namespace.

Don't you think you should learn a bit more about how Python manages
objects and namespaces before going around calling things bugs?


Carl Banks
 
H

horos11

Carl,

Thanks for the info, but a couple of points:

1. it wasn't meant to be production code, simply a way to teach
python.

2. this should either be a compile time or a runtime error.

'Actions at a distance' like this are deadly both to productivity and
to correctness - not only is this a runtime error, it is a *silent*
runtime error. Any other language I know would catch this, whether it
be lua, java, or perl.

Saying that 'whoa, this coding error should be handled by naming
convention' may be the only practical way of getting around this
limitation, but it is a limitation nonetheless, and a pretty big one.

Ed



O
 
H

horos11

It's not a bug.  In Python classes and global variables share the same
namespace.

Don't you think you should learn a bit more about how Python manages
objects and namespaces before going around calling things bugs?

Carl Banks

No, I don't think so..

Say you went to another country, where the people wore lead shoes,
hence not only going slower, but getting lead poisoning from time to
time.

Pointing out that shoes made of fabric might just be better should not
be heresy.

In this case, I think the overall goal was syntax simplicity, but it
sure as hell makes things confusing. No warning, or anything. The sane
behavior IMO would be to disallow the assignment unless put through a
special function, something like:

class(state) = ...

After all, python does have a precedent when you try to join, when:

":".join([1,2])

does not work because [1,2] is an array of ints, whereas

":" . join( str(x) for x in [1,2])

does.

Ed
 
C

Carl Banks

Carl,

Thanks for the info, but a couple of points:

    1. it wasn't meant to be production code, simply a way to teach
python.

I understand, and if you think it's overkill for your pedagogical
application then feel free not to follow the suggestions I gave you.

    2. this should either be a compile time or a runtime error.

'Actions at a distance' like this are deadly both to productivity and
to correctness - not only is this a runtime error, it is a *silent*
runtime error. Any other language I know would catch this, whether it
be lua, java, or perl.

I'm afraid that's just not reasonable given the nature of classes in
Python. Python made a deliberate design trade-off by treating classes
as first-class objects. This decision carries benefits and
drawbacks. You've seen one of the drawbacks. Benefits include
simpler data model and increased opportunities for dynamicism. Perl,
Java, and Lua made the opposite choice, thus they are able to catch
the above mistake.

You could argue that Python made the wrong choice, and that classes
ought not to be first-class objects. Fine. But it didn't, and the
protection you seek is sacrificed.

I suggest looking at tools like PyChecker and PyLint. I don't know if
they'd have found your error in this case, but they can find a lot of
problems Python itself misses.

Saying that 'whoa, this coding error should be handled by naming
convention' may be the only practical way of getting around this
limitation, but it is a limitation nonetheless, and a pretty big one.

I believe it's a miniscule limitation in practice. It seems like a
big deal but doesn't actually happen much. YMMV.


Carl Banks
 
D

Dave Angel

horos11 said:
Carl,

Thanks for the info, but a couple of points:

1. it wasn't meant to be production code, simply a way to teach
python.

2. this should either be a compile time or a runtime error.

'Actions at a distance' like this are deadly both to productivity and
to correctness - not only is this a runtime error, it is a *silent*
runtime error. Any other language I know would catch this, whether it
be lua, java, or perl.

Saying that 'whoa, this coding error should be handled by naming
convention' may be the only practical way of getting around this
limitation, but it is a limitation nonetheless, and a pretty big one.

Ed
The bug in your code that we're discussing is caused because you rebound
a global attribute to a new value, and lost the original object.. Your
complaint is that when the global name happens to reference a class, and
the new value doesn't, you think the compiler should give an error.

If you would call this a limitation of Python, and think it should be
"fixed," then you clearly do not understand the language or its
philosophy. I can't compare to either lua or perl, but Java is a vastly
different language from Python, and this type of thing is at the heart
of the difference. In Java you must declare the types of everything at
compile time (except they had to make a big hole for collections), and
the compiler is therefore told much about the possible behavior of each
variable.

In Python you declare nothing, and much of the power of the language
comes from the fact that every construct is a first-class object.
Function definitions are fancy literal values for function objects,
class declarations are just one of the ways of making a factory
function, and classes themselves are instances of metaclasses. You can
declare new objects that mimic existing ones without having to define a
new class that derives from the old class (duck-typing). And you can
frequently reuse the runtime semantics intended for one purpose to
accomplish something very different.

Python also deliberately doesn't have a notion of "constant"
declaration, nor "private." It doesn't have the notion of a library (or
class) building a "sealed" implementation which cannot be customized.

What Python offers for many of these is some conventions. Names
starting with leading underscores are "private" by convention. Names
all uppercase are "constant." Names starting with an uppercase letter
are classes. But rather than trying to make a language definition of
each of these terms, it's just a convention, observed among the
consenting adults (?) cooperating in the codebase.

I'm not saying (here) whether Python is better or worse than the
languages which are strongly typed at compile time, but just that it's a
deliberate set of design decisions. And special-casing one or two of
the more obscure danger spots is not in keeping with the Python
philosophy. Further, I don't see that you did anything that the runtime
should disallow. You assigned a new value to the 'state' attribute.
And when you tried to call it, it called the __str__() method of that
object.

DaveA
 
H

Hendrik van Rooyen

Saying that 'whoa, this coding error should be handled by naming
convention' may be the only practical way of getting around this
limitation, but it is a limitation nonetheless, and a pretty big one.

You misunderstand the dynamic nature of python - YOU rebound the name to
something else - how on earth can the interpreter know that this is not what
you meant?

Could you formulate a rule that the interpreter should follow to be able to
flag what you have done as an error?

- Hendrik
 
C

Carl Banks

No, I don't think so..

Say you went to another country, where the people wore lead shoes,
hence not only going slower, but getting lead poisoning from time to
time.

Pointing out that shoes made of fabric might just be better should not
be heresy.

This analogy is wrong on two counts:

1. Pointing out that the "fabric might be better" is not analogous to
what you did. You claimed that Python's behavior was a bug, which
would be analogous to me saying, "lead shoes should be illegal".

2. I know enough about the properties of fabric and lead to make an
informed complaint over the use of lead for shoes, even if I am new to
the country. However you very clearly have a poor understanding of
Python's namespace and object models, and so you have little standing
to claim that Python is buggy in how it treats its namespaces.

I'd say the strongest claim you have grounds to make, given the level
of understanding you've shown, is "this is very confusing to noobs".

BTW, the Python maintainers are very particular to define "bug" as
"Python behaves differently than it is documentated to", and Python is
most definitely documented as acting the way it does.

In this case, I think the overall goal was syntax simplicity, but it
sure as hell makes things confusing. No warning, or anything. The sane
behavior IMO would be to disallow the assignment unless put through a
special function, something like:

class(state) = ...
After all, python does have a precedent when you try to join, when:

":".join([1,2])

does not work because [1,2] is an array of ints, whereas

":" . join( str(x) for x in [1,2])

does.

No, it really isn't a precedent. Yes, Python does type-checking quite
a bit, however there is no precedent at all for customizing assignment
to a regular variable. When you write "a = b", a gets bound to the
same object as b is bound to, end of story. You can't customize it,
you can't prevent it, you can hook into it, and neither object is
notified that an assignment is happening. This is true even if a or b
happen to be a class.

And if you say, "Well you should be able to customize assigment", my
answer is, "You were saying there was a precedent, I said there was
not". If you want to argue that it should be possible to customize
assignment, be my guest, but you'll have to do it without the benefit
of a precedent.

(You can customize assignment to an attribute or item, but that is not
what we're talking about, especially since classes are hardly ever
attributes or items.)


Carl Banks
 
S

Simon Forman

Like I said, the code was a sample of what I was trying to do, not the
entire thing.. I just wanted to see if the metaphor was kosher.

Right, but it doesn't help if you post code that doesn't actually
display the unexpected behaviour you're asking about.
It sounds to me from your answer that this is unexpected behavior, so
I'll go ahead and post the whole thing. My guess is that it is a
python bug..

/What/ is unexpected behaviour? I ran the code you posted and worked
exactly like I expected.

Posting the whole thing is better than posting code that doesn't
display the issue, but this is too long. I'd ask you to repost a
minimal version that actually displays the problem you're asking about
but I see from the rest of this thread that you've already figured it
out.

Related to that, two points.

Learn python AS python, don't get caught up in what it does
differently than other languages. IMHO it's hands-down the most
useful. productive language out there for a tremendous number of
problem domains. Treat yourself to it. ;]

And second, please don't /teach/ python until you've learned it...

Regards,
~simon
Run it (a simple puzzle game solved by breadth first search), and the
first time state() is called inside the method, it calls __init__.
Second time, and therafter, it calls __call__. I've highlighted where
the code fails by putting a pdb.set_trace().

Anyways, I've got a workaround (simply pass in any new objects needed
from the caller), but it is truly annoying that python is either
misleading or broken in this way.

Attached find code, does not work vs. 2.6..


Ed

----

from collections import deque
import copy
import pdb

class state:

   def default_board():

       return [
             [ 1, 'x', 'x', 0 ],
             [ 2, 2,  3,  4 ],
             [ 5, 6,  6,  7 ],
             [ 5, 6,  6,  7 ],
             [ 8, 9, 10, 10 ],
             [ 0, 'x', 'x', 0 ]
           ]

   def default_types():

       return {
               1  : [ 0, 0 ],
               2  : [ 0, 0, 0, 1 ],
               3  : [ 0, 0 ],
               4  : [ 0, 0 ],
               5  : [ 0, 0, 1, 0 ],
               6  : [ 0, 0, 1, 0, 0, 1, 1, 1 ],
               7  : [ 0, 0, 1, 0 ],
               8  : [ 0, 0 ],
               9  : [ 0, 0 ],
               10 : [ 0, 0, 0, 1 ]
           }

   def default_moves():

       return []

   def print_move(self, moveno, move):
       print str(moveno) + ": " + str(move) + "\n"


   def __init__(self, _board=default_board(), _moves=default_moves(),
_types=default_types()):

       self.board = _board
       self.moves = _moves
       self.types = _types

   def possible_moves(self):

       moves_so_far = set()
       moves_so_far.add('x')
       moves_so_far.add(0)
       ret = []
       for y_idx in range(0, len(self.board)):
           for x_idx in range(0, len(self.board[y_idx])):

               piece = self.board[y_idx][x_idx]

               if not piece in moves_so_far:

                   moves = self.legal_moves(y_idx, x_idx)
                   moves_so_far.add(piece)

                   if moves:
                       ret.extend(moves)

       return ret

   def is_answer(self):

       if self.board[5][3] == 1:
           return True
       else:
           return False

   def legal_moves(self, ycoord, xcoord):

       ret = []
       for dir in [ [ 0, 1 ], [ 0, -1 ], [ 1, 0 ], [ -1, 0 ] ]:
           ret.extend(self.addmove(dir[0], dir[1], ycoord, xcoord))

       return ret

   def empty(self, type, ycoord, xcoord, pieceno):

       for itr in range(0, len(type), 2):

           yy = type[itr]
           xx = type[itr+1]

           if not (len(self.board) > (yy+ycoord) >= 0)  or not (len
(self.board[yy+ycoord]) > xx+xcoord >= 0):
               return False

           if not self.board[yy+ycoord][xx+xcoord] in [ 0, pieceno ]:
               return False

       return True

   def addmove(self, ymult, xmult, ycoord, xcoord):

       ret = []
       pieceno = self.board[ycoord][xcoord]
       type    = self.types[pieceno]

       if xmult != 0:
           for xx in range(xcoord + xmult, -1 if xmult < 0 else 4, -1
if xmult < 0 else 1):
#               if xx == 0:
#                   continue
               if self.empty(type, ycoord, xx, pieceno):
                   ret.append(self.newmove(ycoord, xcoord, ycoord,
xx ))
               else:
                   break

       if ymult != 0:
           for yy in range(ycoord + ymult, -1 if ymult < 0 else 6, -1
if ymult < 0 else 1):
#               if yy == 0:
#                   continue
               if self.empty(type, yy, xcoord, pieceno):
                   ret.append(self.newmove(ycoord, xcoord, yy,
xcoord))
               else:
                   break

       return ret

   def newmove(self, fromy, fromx, toy, tox):

       move = {
               'fromx' : fromx,
               'fromy' : fromy,
               'toy'   : toy,
               'tox'   : tox,
               'piece' : self.board[fromy][fromx]
               }

       return move

   def printout_path(self):

#       print self
       pdb.set_trace()
       answer = state()

       moveno = 0
       print "\n==========================\n"

       for moveno in range(0, len(self.moves)):
           move = self.moves[moveno]
           self.print_move(moveno, move)
           answer.apply_move(move)
           answer.print_board()
       print "\n==========================\n"

   def print_board(self):

       for xx in self.board:

           print ": ".join([ "%2s" % str(ii) for ii in xx ])

   def to_string(self):

       return str(self.board)

   def apply_move(self, move):

       self.moves.append(move)
       num = self.board[move['fromy']][move['fromx']]
       type = self.types[num]

       for idx in range(0,len(type),2):
           yy_delta = type[idx]
           xx_delta = type[idx+1]
           self.board[move['fromy']+ yy_delta][move['fromx']+
xx_delta] = 0

       for idx in range(0,len(type),2):
           yy_delta = type[idx]
           xx_delta = type[idx+1]
           self.board[move['toy']+ yy_delta][move['tox']+ xx_delta] =
num

   def next_states(self):

       ret = []
       for move in self.possible_moves():
           newstate = copy.deepcopy(self)
           newstate.apply_move(move)
           ret.append(newstate)

#        pdb.set_trace()
#        for xx in ret:
#            print xx.__dict__
#            print "\n"

       return ret

seen_states = set()

start = state()
dq    = deque()

dq.append(start)


while len(dq):

   curstate = dq.popleft()
   print "HERE " + str(curstate) + " => " + curstate.to_string() +
"\n"
   curstate.printout_path()

   if curstate.is_answer():
       curstate.printout_path()

   elif curstate.to_string() in seen_states:
       print "DUP " + curstate.to_string()

   else:
       seen_states.add(curstate.to_string())
       for state in curstate.next_states():
           if not state.to_string() in seen_states:
               dq.append(state)

               print "Trying..\n"
               state.print_board()
               print "\n"
 
H

horos11

Speaking as someone who does teach Python, "Ew, no!"  If you start by
teaching people bad habits, every educator who comes along afterwards
will curse your name.  That includes teaching yourself.

No offense, but I disagree. By programming without regards to pre-
existing style or convention I learned far more than I otherwise would
have if I had simply mimicked someone else.

And I still think that unbridled assignment - especially assignment
that can change the operational semantics of surrounding terms, at a
distance no less - is a horrid thing.

It gets even worse because the way python handles assignment. To go
back to my original program: why isn't the state variable that I
defined local to that 'if' loop?

while len(dq):

...
if curstate.is_answer():
...
else:
for state in ...


The answer? Because you can't explicitly declare it. It therefore
looks globally, finds the 'class state:' statement, and runs with it.
I should be able to say:

for my state in curstate.next_states():

to show explicitly what I'm doing.


Anyways, maybe I got off to a bad start, but I'm a bit leery of the
language. In my estimation it's trying to be 'too clever by half', and
this coming from a veteran bash/perl programmer. I mean, free form is
one thing, but too much of a good thing can be harmful to your
programming health. Maybe PyChecker or PyLint will help, I don't know.

Ed

(
ps - an aside, but what was the rationale behind only displaying one
error at a time on trying to run a script? I typically like to run a
compilation phase inside my editor (vim), get a list of errors, and
then go to each one and fix them.

And how do you just check a script's syntax without running it
anyways?
)
 
B

Benjamin Kaplan

No offense, but I disagree. By programming without regards to pre-
existing style or convention I learned far more than I otherwise would
have if I had simply mimicked someone else.

And I still think that unbridled assignment - especially assignment
that can change the operational semantics of surrounding terms, at a
distance no less - is a horrid thing.

It gets even worse because the way python handles assignment. To go
back to my original program: why isn't the state variable that I
defined local to that 'if' loop?

while len(dq):

   ...
   if curstate.is_answer():
       ...
   else:
       for state in ...


The answer? Because you can't explicitly declare it. It therefore
looks globally, finds the 'class state:' statement, and runs with it.
I should be able to say:

   for my state in curstate.next_states():

to show explicitly what I'm doing.


Anyways, maybe I got off to a bad start, but I'm a bit leery of the
language. In my estimation it's trying to be 'too clever by half', and
this coming from a veteran bash/perl programmer. I mean, free form is
one thing, but too much of a good thing can be harmful to your
programming health. Maybe PyChecker or PyLint will help, I don't know.

Ed

(
ps - an aside, but what was the rationale behind only displaying one
error at a time on trying to run a script? I typically like to run a
compilation phase inside my editor (vim), get a list of errors, and
then go to each one and fix them.

And how do you just check a script's syntax without running it
anyways?
)

Because these aren't compile-time errors. Python has no compilation
phase- every statement (including def and class) is an executable
statement and it gets turned into byte code at execution time. Just
like any other language, when Python hits a runtime error, it stops.
 
C

Carl Banks

Because these aren't compile-time errors. Python has no compilation
phase- every statement (including def and class) is an executable
statement and it gets turned into byte code at execution time. Just
like any other language, when Python hits a runtime error, it stops.

No, there is a compile phase, but the only error that is raised at
compile-time is SyntaxError. Because of Python's dynamicism the
compiler knows hardly anything about the objects at compile-time
(except in a few cases involving constants, which Python takes
advantage of to do some compile-time constant folding).


Carl Banks
 
C

Carl Banks

      * You define a to_string() method. To have a string representation
        of a class, one usually defines a __str__ method.  This gives
        the advantage whereby "print myobject" or '%s' % myjobject just
        work.


In fairness, a lot of types define a to_string() method (especially
third-party types like numpy), but it's usually to produce a raw
binary output.

Presumably in Python 3 these methods will be renamed to_bytes.


Carl Banks
 
S

Simon Forman

No offense, but I disagree. By programming without regards to pre-
existing style or convention I learned far more than I otherwise would
have if I had simply mimicked someone else.

Don't teach newbies bad idiom.
And I still think that unbridled assignment - especially assignment
that can change the operational semantics of surrounding terms, at a
distance no less - is a horrid thing.

That's opinion.

Python allows you to shoot yourself in the foot. It's on us as
programmers to be able to grok our code well enough to prevent nasty
errors. "With great power comes great responsibility." and all that.
It gets even worse because the way python handles assignment. To go
back to my original program: why isn't the state variable that I
defined local to that 'if' loop?

There's no such thing as an "if loop".

The only answer to your question is "that's the way it is"... or,
equivalently, "that's the way Guido made it."

Check out "Python Scopes and Name Spaces"
http://www.python.org/doc/2.2/tut/node11.html#SECTION0011200000000000000000
while len(dq):

   ...
   if curstate.is_answer():
       ...
   else:
       for state in ...


The answer? Because you can't explicitly declare it. It therefore
looks globally, finds the 'class state:' statement, and runs with it.
I should be able to say:

   for my state in curstate.next_states():

to show explicitly what I'm doing.

You can, you just have to be careful not to "shadow" some other name
in your namespace(s). Again, python makes a trade off between
"babysitting" the programmer on the one hand vs. raw flexibility on
the other.
Anyways, maybe I got off to a bad start, but I'm a bit leery of the
language. In my estimation it's trying to be 'too clever by half', and
this coming from a veteran bash/perl programmer. I mean, free form is
one thing, but too much of a good thing can be harmful to your
programming health. Maybe PyChecker or PyLint will help, I don't know.

I think if you let python seep into your thinking you'll eventually
come to love it.

It /IS/ very (too) clever, but the cleverness is (IMHO) spent on
getting out of your way, rather than trying to insure you're doing
things right.

Eric Raymond has a great article where he talks about learning python:
http://www.linuxjournal.com/article/3882

"My second [surprise] came a couple of hours into the project, when I
noticed (allowing for pauses needed to look up new features in
Programming Python) I was generating working code nearly as fast as I
could type. When I realized this, I was quite startled. An important
measure of effort in coding is the frequency with which you write
something that doesn't actually match your mental representation of
the problem, and have to backtrack on realizing that what you just
typed won't actually tell the language to do what you're thinking. An
important measure of good language design is how rapidly the
percentage of missteps of this kind falls as you gain experience with
the language."

I can vouch for this: I can routinely write 200~300 lines of python
code and have it run flawlessly the first time. This doesn't happen
every time, but it's the norm rather than the exception.


Give it a chance. It's (IMHO) /beautiful/.


Happy hacking,
~Simon

Ed

(
ps - an aside, but what was the rationale behind only displaying one
error at a time on trying to run a script? I typically like to run a
compilation phase inside my editor (vim), get a list of errors, and
then go to each one and fix them.

I dunno the rationale, but the mechanism is uncaught exceptions
propagate up and halt the interpreter. Since nothing keeps going
after an uncaught exception, there's nothing there to report the next
error.
And how do you just check a script's syntax without running it
anyways?

Just run it. ;]

FWIW, use the interactive interpreter (or IPython variant) to play
with the language until you learn the syntax well enough not to write
bad syntax. For me anyway, that was the best/fastest way to learn it.
 
T

Terry Reedy

horos11 said:
Anyways, maybe I got off to a bad start,

Blaming programming errors on non-existent bugs in the interpreter is
not a way to endear yourself.

And perhaps Python truly is not your style.
Maybe PyChecker or PyLint will help, I don't know.

I do not use them, but others swear by them.
ps - an aside, but what was the rationale behind only displaying one
error at a time on trying to run a script? I typically like to run a
compilation phase inside my editor (vim), get a list of errors, and
then go to each one and fix them.

It would complicate the compiler. It is consistent with the rest of
Python's error reporting system (one runtime error only also). Error
reports after the first may be bogus. Python aware editors, like the one
with IDLE, put the cursor at the reported location of a syntax error.
And they only have one cursor ;-).
And how do you just check a script's syntax without running it
anyways?

The checker programs parse the code, so I would expect they report
syntex errors.

Or: put $, a syntax error, on a line at the bottom of your script to
abort execution. If the interpreter stops there, everything before is
syntacally OK. If using a Python-aware editor, you will pop back into
the edit window at the bottom of the file.

But of course, not running means not running the test code, so the
program is not necessarily OK at all.

Terry Jan Reedy
 

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
473,755
Messages
2,569,538
Members
45,024
Latest member
ARDU_PROgrammER

Latest Threads

Top