473,407 Members | 2,598 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,407 software developers and data experts.

Class Variable Access and Assignment

This has to do with class variables and instances variables.

Given the following:

<code>

class _class:
var = 0
#rest of the class

instance_b = _class()

_class.var=5

print instance_b.var # -> 5
print _class.var # -> 5

</code>

Initially this seems to make sense, note the difference between to last
two lines, one is refering to the class variable 'var' via the class
while the other refers to it via an instance.

However if one attempts the following:

<code>

instance_b.var = 1000 # -> _class.var = 5
_class.var = 9999 # -> _class.var = 9999

</code>

An obvious error occurs. When attempting to assign the class variable
via the instance it instead creates a new entry in that instance's
__dict__ and gives it the value. While this is allowed because of
pythons ability to dynamically add attributes to a instance however it
seems incorrect to have different behavior for different operations.

There are two possible fixes, either by prohibiting instance variables
with the same name as class variables, which would allow any reference
to an instance of the class assign/read the value of the variable. Or
to only allow class variables to be accessed via the class name itself.

Many thanks to elpargo and coke. elpargo assisted in fleshing out the
best way to present this.

perhaps this was intended, i was just wondering if anyone else had
noticed it, and if so what form would you consider to be 'proper'
either referring to class variables via the class itself or via
instances of that class. Any response would be greatly appreciated.
Graham

Nov 3 '05
166 8456
Graham wrote:
Thanks to all of you for your reply's i had no idea it would get this
sort of response,
i've read through most of the posts and it seems that its a active
topic.

My question remains however, i suppose i'm not familiar with how this
functions in
other languages, but what is the common way of referring to a class
variable.

is <class>.<var> the norm?
or <instance>.<var> the norm.
Let me try to give a simple answer to a simple question before you begin
to feel that we are all barmy and maybe Perl would be an easier
alternative :-)

Under normal circumstances, yes, if you want to reference and/or modify
a value that's intended to be shared amongst all instances of a given
class you would normally refer to that as

classname.var

and such a reference will work inside any of the class's methods.

Inside the class body (but outside any method body) the same reference
can be written as

var
I just seems to me that <instance>.<var> shouldn't defer to the class
variable if
an instance variable of the same name does not exists, it should, at
least how i
understand it raise an exception.

Is my thinking way off here?

It's a long way from how Python is designed, because name resolution in
Python will look in an instance's class (and then the class's
superclass, and so on until object is reached, which is at the top of
all modern class hierarchies) unless the name is first found in the
instance's namespace.

That's just the way Python was designed. Like it or loathe it, it's too
late to change now. <shrug>. Some languages only use such a technique
for resolving the names associated with methods, but this isn't
comp.lang.someotherlanguage :-)

pragmatical-ly y'rs - steve
--
Steve Holden +44 150 684 7255 +1 800 494 3119
Holden Web LLC www.holdenweb.com
PyCon TX 2006 www.python.org/pycon/

Nov 4 '05 #51
Antoon Pardon wrote:
Op 2005-11-03, Stefan Arentz schreef <st***********@gmail.com>: [...]
It is all according to how things have been in Python for a long time.


Unsane behaviour for a long time is still unsane behaviour.

As your continued contributions on this newsgroup so adequately
demonstrate :-).

Sorry, I *couldn't* resist. You asked for it. It was hanging there (in a
containing namespace?) waiting to be posted. If I hadn't said it someone
else would have. And other justifications for what I hope doesn't seem
like too unpleasant a personal attack.
The real issue here is that you should propery name class variables so
that there can't be any confusion about class or instance scope. I use
all uppercase identifiers for class variables for example.

The fact that this can be regarded as unwise coding, doesn't imply
it is sane behaviour of python. Variable shadowing happens. I don't
consider it sane behaviour if the same reference in a line gets
resolved in different name spaces

Well I'm sure Guido will be happy to know you think his design is
insane. Now who's calling who names?

regards
Steve
--
Steve Holden +44 150 684 7255 +1 800 494 3119
Holden Web LLC www.holdenweb.com
PyCon TX 2006 www.python.org/pycon/

Nov 4 '05 #52
Op 2005-11-03, Stefan Arentz schreef <st***********@gmail.com>:
Antoon Pardon <ap*****@forel.vub.ac.be> writes:
Op 2005-11-03, Stefan Arentz schreef <st***********@gmail.com>:
> Antoon Pardon <ap*****@forel.vub.ac.be> writes:
>
> ...
>
>> Fine, we have the code:
>>
>> b.a += 2
>>
>> We found the class variable, because there is no instance variable,
>> then why is the class variable not incremented by two now?
>
> Because it really is executed as:
>
> b.a = b.a + 2
That is an explanation, not a reason.


I'm just following the
> 1. get 't'b.a and store it in a temporary 't' (found the instance)
> 2. add 2 to 't'
> 3. store 't' in 'b.a'
>
> The last operation stores it into an instance variable.


[ I think you mean '(found the class variable)' in line 1 ]

All you are doing here is explain how the current implemantation treats
this. You are not giving arguments for why the result of this
implementation should be considered sane behaviour.


Ah yes. Well, good luck with that. You seem to have decided that it is not
sane and who am I to argue with that. It depends on your state of mind :-)


I can just say the opposite, that you seem to have decided that it is
sane.
The model makes sense in my opinion. If you don't like it then there are
plenty of other languages to choose from that have decided to implement
things differently.


And again this argument. Like it or leave it, as if one can't in general
like the language, without being blind for a number of shortcomings.

It is this kind of recations that make me think a number of people is
blindly devoted to the language to the point that any criticism of
the language becomes intollerable.

--
Antoon Pardon
Nov 4 '05 #53
Once again, many thanks, your explainations are very detailed and i
think i'm in full understanding of the what/when/why of it all.

And with further introspection i can see why its done this way from a
language processing point of view rather than programming one. I also
now realize that <instance>.<classvarname> is there so that you dont
have to type <instance>.__class__.<varname> all the time.

Once again many thanks, i hadn't expected nearly this type of response.
Graham.

Nov 4 '05 #54
Op 2005-11-03, Steven D'Aprano schreef <st***@REMOVETHIScyber.com.au>:
On Thu, 03 Nov 2005 14:13:13 +0000, Antoon Pardon wrote:
Fine, we have the code:

b.a += 2

We found the class variable, because there is no instance variable,
then why is the class variable not incremented by two now?
Because b.a += 2 expands to b.a = b.a + 2. Why would you want b.a =
<something> to correspond to b.__class__.a = <something>?


That is an implemantation detail. The only answer that you are given
means nothing more than: because it is implemented that way.
I'm not saying that it couldn't, if that was the model for inheritance you
decided to use. I'm asking why would you want it? What is your usage case
that demonstrates that your preferred inheritance model is useful?


It has nothing to do with a model for inheritance, but with a model of
name resolution.

The hierarchie of searching an instance first in an object and then in
a class isn't that different from searching first in a local namespace
and then in a more global namespace.

When we search names in a function we don't resolve the same name in
different name spacese each occurence of the same name in the same
function occurs in the same namespace.

But with class variables we can have that one and the same name
on a line refers to two different namespaces at the same time.
That is IMO madness. You may argue that the madness is of little
importance, you can argue that because of the current implementation
little can be done about it. But I don't see how one can defend
it as sane behaviour.

--
Antoon Pardon
Nov 4 '05 #55
Op 2005-11-04, Steve Holden schreef <st***@holdenweb.com>:
Antoon Pardon wrote:
Op 2005-11-03, Stefan Arentz schreef <st***********@gmail.com>:

[...]
It is all according to how things have been in Python for a long time.


Unsane behaviour for a long time is still unsane behaviour.

As your continued contributions on this newsgroup so adequately
demonstrate :-).

Sorry, I *couldn't* resist. You asked for it. It was hanging there (in a
containing namespace?) waiting to be posted. If I hadn't said it someone
else would have. And other justifications for what I hope doesn't seem
like too unpleasant a personal attack.


Well I would argue that a lot of the defenders of python are not
reacting very sane. My impression is that a lot of them react like
zealots, blindly devoted to the language, rather intollerant of
every criticism and prepared to defend anything as long as it happens
to be a current characteristic of the language and where any such
criticism sooner or later is met with something like: "If you don't
like it, use a different language", as if only those who are 100%
perfectly happy with the language as it is, should use it.
The real issue here is that you should propery name class variables so
that there can't be any confusion about class or instance scope. I use
all uppercase identifiers for class variables for example.

The fact that this can be regarded as unwise coding, doesn't imply
it is sane behaviour of python. Variable shadowing happens. I don't
consider it sane behaviour if the same reference in a line gets
resolved in different name spaces

Well I'm sure Guido will be happy to know you think his design is
insane. Now who's calling who names?


I'm not calling anyone names. I'm just pointing to one specific
behaviour in python and call that behaviour unsane. If you want
to interpret that as me calling his (entire) design insane, I
suggest you are two defensive with regards to python.

--
Antoon Pardon
Nov 4 '05 #56
Op 2005-11-03, Steven D'Aprano schreef <st***@REMOVETHIScyber.com.au>:
On Thu, 03 Nov 2005 04:30:09 -0800, Paul Rubin wrote:
Steve Holden <st***@holdenweb.com> writes:
> class A:
> a = 1
> b = A()
> b.a += 2
> print b.a
> print A.a
> Which results in
> 3
> 1
>
I don't suppose you'd care to enlighten us on what you'd regard as the
superior outcome?


class A:
a = []
b = A()
b.append(3)
print b.a
print a.a

Compare and contrast.

I take it then that you believe that ints like 1 should be mutable like
lists? Because that is what the suggested behaviour implies.


No it isn't.

One other way, to implement the += and likewise operators would be
something like the following.

Assume a getnsattr, which would work like getattr, but would also
return the namespace where the name was found. The implementation
of b.a += 2 could then be something like:

ns, t = getnsattr(b, 'a')
t = t + 2
setattr(ns, 'a')
I'm not arguing that this is how it should be implemented. Just
showing the implication doesn't follow.

--
Antoon Pardon
Nov 4 '05 #57
Op 2005-11-03, Steven D'Aprano schreef <st***@REMOVETHIScyber.com.au>:
On Thu, 03 Nov 2005 12:53:37 +0000, Antoon Pardon wrote:
I don't suppose you'd care to enlighten us on what you'd regard as the
superior outcome?


No. I don't think a superior outcome is necessary to see that this is
not sane behaviour. I don't care that much on how it gets fixed.


It isn't broken, there is nothing to fix. The code does precisely what the
inheritance model promises to do.


That is not a contra argument. Delivering what is promissed says nothing
about the sanity of what is promissed. It is even possible that a number
of things that are sane in itself, produce something unsane when
combined or in certain circumstances.

--
Antoon Pardon
Nov 4 '05 #58
Antoon Pardon <ap*****@forel.vub.ac.be> writes:

....
Ah yes. Well, good luck with that. You seem to have decided that it is not
sane and who am I to argue with that. It depends on your state of mind :-)
I can just say the opposite, that you seem to have decided that it is
sane.


I have. I like the Python model.
The model makes sense in my opinion. If you don't like it then there are
plenty of other languages to choose from that have decided to implement
things differently.


And again this argument. Like it or leave it, as if one can't in general
like the language, without being blind for a number of shortcomings.


Personally I don't see it as a shortcoming.
It is this kind of recations that make me think a number of people is
blindly devoted to the language to the point that any criticism of
the language becomes intollerable.


No not at all. Just look at all the PEPs and the changes in the language
that have been made in the past. Python is very much community driven and
that shows in it's progress.

You on the other hand keep talking about emo things like 'sane' and
'madness' without giving any technical backing about these problems
that you are having with the language.

Snap out of that, make it a real discussion and maybe something good
will happen. Or not :-)

S.
Nov 4 '05 #59
Op 2005-11-03, Magnus Lycka schreef <ly***@carmen.se>:
Antoon Pardon wrote:
There is no instance variable at that point. How can it add 2, to
something that doesn't exist at the moment.


Because 'a += 1' is only a shorthand for 'a = a + 1' if a is an
immutable object? Anyway, the behaviour is well documented.

http://docs.python.org/ref/augassign.html says:

An augmented assignment expression like x += 1 can be rewritten as x = x
+ 1 to achieve a similar, but not exactly equal effect. In the augmented
version, x is only evaluated once.


Then couldn't we expect that the namespace resolution is also done
only once?

I say that if the introduction on += like operators implied that the
same mentioning of a name would in some circumstances be resolved to
two different namespaces, then such an introduction would better have
not occured.

Would it be too much to ask that in a line like.

x = x + 1.

both x's would resolve to the same namespace?

--
Antoon Pardon
Nov 4 '05 #60
Op 2005-11-03, Magnus Lycka schreef <ly***@carmen.se>:
Antoon Pardon wrote:
Op 2005-11-03, Steven D'Aprano schreef <st***@REMOVETHIScyber.com.au>:

There are two possible fixes, either by prohibiting instance variables
with the same name as class variables, which would allow any reference
to an instance of the class assign/read the value of the variable. Or
to only allow class variables to be accessed via the class name itself.

There is also a third fix: understand Python's OO model, especially
inheritance, so that normal behaviour no longer surprises you.

No matter wat the OO model is, I don't think the following code
exhibits sane behaviour:

class A:
a = 1

b = A()
b.a += 2
print b.a
print A.a

Which results in

3
1


On the other hand:
class C: ... a = [1]
... b=C()
b.a += [2]
b.a [1, 2] C.a

[1, 2]

I can understand that Guido was a bit reluctant to introduce
+= etc into Python, and it's important to understand that they
typically behave differently for immutable and mutable objects.


All fine by me. I won't be using python any less because of this,
because I use class variable very little and you can avoid this
problem by avoiding instance that shadow class variables and
always refer to class variables by class name.

But that doesn't mean we should consider this kind of behaviour
as it should be, just because it is in python.

--
Antoon Pardon
Nov 4 '05 #61
Op 2005-11-03, Steven D'Aprano schreef <st***@REMOVETHIScyber.com.au>:
On Thu, 03 Nov 2005 13:01:40 +0000, Antoon Pardon wrote:
Seems perfectly sane to me.

What would you expect to get if you wrote b.a = b.a + 2?
I would expect a result consistent with the fact that both times
b.a would refer to the same object.


class RedList(list):
colour = "red"

L = RedList(())

What behaviour would you expect from len(L), given that L doesn't have a
__len__ attribute?


Since AFAICT there is no single reference to the __len__ attribute that
will be resolved to two different namespace I don't see the relevance.
Why do you expect
b.a += 2 to give a different result?


I didn't know I did.


It seems to me that you do.

You didn't appear to be objecting to a line like x = b.a assigning the
value of 1 to x (although perhaps you do). If that was the case, then it
is perfectly reasonable to expect b.a = x + 2 to store 3 into b.a, while
leaving b.__class__.a untouched.

Of course, if you object to inheritance, then you will object to x = b.a
as well.


What I object to is that the mentioning of one instance gets resolved
to two different namespaces.
Since ints are immutable objects, you shouldn't expect the value of b.a
to be modified in place, and so there is an assignment to b.a, not A.a.


You are now talking implementation details. I don't care about whatever
explanation you give in terms of implementation details. I don't think
it is sane that in a language multiple occurence of something like b.a
in the same line can refer to different objects


That's an implementation detail only in the sense that "while condition"
is a loop is an implementation detail. It is a *design* detail.

b is a name, and any reference to b (in the same namespace) will refer
to the same object. At least until you rebind it to another object.
But some namespaces take great care not to allow a rebinding that would
result in the same name being resolved to a different namespace during
this namespace's lifetime.
But b.a is not a name, it is an attribute lookup,
An other implementation detail. b.a is a name search of 'a' in the
namespace b.
and by Python's rules of
inheritance that lookup will look up attributes in the instance, the
class, and finally any superclasses. If you persist in thinking of b.a as a name referring to a single object,
of course you will be confused by the behaviour. But that's not what
attribute lookup does. On the right hand side of an assignment, it will return the first existing
of b.__dict__['a'] or b.__class__.__dict__['a']. On the left hand of an
assignment, it will store into b.__dict__['a'].


That holly python does it this way, doesn't imply it is reasonable to
do it this way or that all consequences of doing it this way are
reasonable.
I think it even less sane, if the same occurce of b.a refers to two
different objects, like in b.a += 2


Then it seems to me you have some serious design problems. Which would you
prefer to happen?

# Scenario 1
# imaginary pseudo-Python code with no inheritance:
class Paragraph:
ls = '\n' # line separator

para = Paragraph()
para.ls

=> AttributeError - instance has no attribute 'ls'
# Scenario 2
# imaginary pseudo-Python code with special inheritance:
class Paragraph:
ls = '\n' # line separator

linux_para = Paragraph()
windows_para = Paragraph()
windows_para.ls = '\n\r' # magically assigns to the class attribute
linux_para.ls

=> prints '\n\r'

# Scenario 3
# Python code with standard inheritance:
class Paragraph:
ls = '\n' # line separator

linux_para = Paragraph()
windows_para = Paragraph()
windows_para.ls = '\n\r'
linux_para.ls

=> prints '\n'


I don't see the relevance of these pieces of code. In none of them is
there an occurence of an attribute lookup of the same attribute that resolves
to different namespaces.

--
Antoon Pardon
Nov 4 '05 #62
Op 2005-11-03, Steven D'Aprano schreef <st***@REMOVETHIScyber.com.au>:
On Thu, 03 Nov 2005 13:35:35 +0000, Antoon Pardon wrote:
Suppose I have code like this:

for i in xrange(1,11):
b.a = b.a + i

Now the b.a on the right hand side refers to A.a the first time through
the loop but not the next times. I don't think it is sane that which
object is refered to depends on how many times you already went through
the loop.


Well, then you must think this code is *completely* insane too:

py> x = 0
py> for i in range(1, 5):
... x += i
... print id(x)
...
140838200
140840184
140843160
140847128

Look at that: the object which is referred to depends on how many times
you've already been through the loop. How nuts is that?


It is each time the 'x' from the same name space. In the code above
the 'a' is not each time from the same namespace.

I also think you new very well what I meant.

--
Antoon Pardon
Nov 4 '05 #63
Op 2005-11-03, Mike Meyer schreef <mw*@mired.org>:
Antoon Pardon <ap*****@forel.vub.ac.be> writes:
What would you expect to get if you wrote b.a = b.a + 2? I would expect a result consistent with the fact that both times
b.a would refer to the same object.


Except they *don't*. This happens in any language that resolves
references at run time.


Python doesn't resolve references at run time. If it did the following
should work.

a = 1
def f():
a = a + 1

f()

But letting that aside. There is still a difference between resolving
reference at run time and having the same reference resolved twice
with each resolution a different result.
Changing that would be changing a fundamental
- and *important* - feature of Python. Arbitrary restrictions to
prevent a single case of this from doing something people who aren't
used to suvh behavior are kludges, and would constitute a wart on the
language, pure and simple.
Python already has its warts. If you want to argue that fixing this
would make a bigger wart then the wart it is now. Fine I will accept
that.
If you think this is bad, you should consider Jensen's device. It uses
call-by-name, which Python doesn't have.


Actually, I would have thought it very interesting should python
have provided some choice in parameter semantics.

I think it even less sane, if the same occurce of b.a refers to two
different objects, like in b.a += 2


That's a wart in +=, nothing less. The fix to that is to remove +=
from the language, but it's a bit late for that.

<mike


Well we agree that there is a wart somewhere.

--
Antoon Pardon
Nov 4 '05 #64
Op 2005-11-04, Steve Holden schreef <st***@holdenweb.com>:
Antoon Pardon wrote:
Op 2005-11-03, Stefan Arentz schreef <st***********@gmail.com>:
Antoon Pardon <ap*****@forel.vub.ac.be> writes:

...
>>No matter wat the OO model is, I don't think the following code
>>exhibits sane behaviour:
>>
>>class A:
>> a = 1
>>
>>b = A()
>>b.a += 2
>>print b.a
>>print A.a
>>
>>Which results in
>>
>>3
>>1
>
>I find it confusing at first, but I do understand what happens :-)

I understand what happens too, that doesn't make it sane behaviour.
>But really, what should be done different here?

I don't care what should be different. But a line with only one
referent to an object in it, shouldn't be referring to two different
objects.

It doesn't.

Yes it does. If the b.a refers to the instance variable, then an
AttributeError should be raised, because the instance variable doesn't
exist yet, so you can't add two to it.

Excuse me. The statement

a += 2

causes a to refer to a different object after the assignment than it did
before. So does the statement


But the 'a' is both times in the same namespace.
self.a += 2
In this case the 'a' is not necessarily both times in the same
name space.
So why are you so concerned that the pre-assignment reference comes from
a different scope to the post-assignment reference? The fact remains
that after both assignments the rebound name can no longer (ever) be
used to refer to its former referent without a further rebinding taking
place.
I concerned with the a refering to different variables. A variable being
a name in a specific namespace.
If the b.a refers to the class variable then two should be added to it.

Wring, wring, wring. (Sorry, got that wrong :-)
Neither happens instead we get some hybrid in which an instance varible
is created that gets the value of class variable incrented by two.

Yes. So does this mean you also have a problem with

def f(x):
x += 2

g = 3
print f(g)

When the function call executes, the name x is bound to an object in the
call's containing scope. Is it then your contention that the augmented
assignment in the function should add two to that object, changing the
value of g?


Whether I have a problem with this specific behaviour or not is
irrelevant. In this case we have only one namespace with an 'x'.
So searching for 'x' will not result in different variables being
found.
It doesn't, it simply proceeds along the lines of all Python assignments
and resolves the name as a reference to a specific object. It then
computes a new value from the referenced object and the augmented
assignment operator's right operand, and rebinds the name to the
newly-computed value.

Please stop talking about variables.
No I think variable is the right term here. It refers to a name
in a specific namespace.
Although augmented assignment operators have the *option* of updating
objects in place, surely not even you can require that they do so when
they are bound to an immutable object such as an integer.
No but I can require that the namespace to which a name is resolved,
doesn't change during the operation.
It doesn't touch the *class variable* A.a which is still 1.

Why "should" it? Why, why, why? And gain, just for good measure, why?
Augmented assignment to a function argument doesn't modify the passed
object when immutable, and you have no problem with that (I know as I
write that this is just asking for trouble, and it will turn out that
you also find that behavior deeply controversial ...)

But it accesses the class variable.

Repeat after me: "Python assignment binds values to names".


But not just to a name, it binds a name in a specific namespace.
When I write

class something:
a = 1
def __init__(self, val=None):
if val:
self.a += val

then in the last statement the augmented assignment rebinds "self.a"
from the class "variable" to a newly-created instance "variable". I am
of course using the word "variable" here in a Pythonic sense, rather
than in the sense that, say, a C programmer would use. In Python I
prefer to talk about binding names because talking of variables leads
people to expect that a name is bound to an area of memory whose value
is modified by assignment, but this is in fact not so.

The initial access to self.a uses the defined name resolution order to
locate a value that was bound to the name "a" in class scope. So what?
This is a long-documented fact of Python life. It's *supposed* to be
that way, dammit.
That it is documented, doesn't make it sane behaviour. Otherwise
all companies had to do was to document there bugs.

In a line like b.a += 2, you only have one reference to a name
to be resolved in a spefied namespace (hierarchy). Since there
is only one reference I don't think it is sane that two resolutions
are done with two different variables as a result.
I fail to understand why this is such a problem for you. But then it's
clear from long past experience that our perceptions of Python's
execution model differ quite radically, and that I seem to find it quite
satisfactory overall, whereas you are forever banging on about what
"should" be true of Python and what Python "should" do. Which, as is
probably obvious by now, I sometimes find just a teeny bit irritating.
Kindly pardon my tetchiness.

I suppose ultimately I'm just more pragmatic than you.
It has nothing to do with being more pragmatic. Being pragmatic
is about how you handle things with real life projects. It has
little to do with the degree in which you agree with the design
of the tool you have to work with. I would say I am more pragmatic
than most defenders of python, because when it comes done to
do my work, I just use python as best as I can, while a lot
of people here seem to think that every little criticism I have
is enough to go and look for a different language.
Plus I started
using Icon, whose assignment semantics are very similar, back in the
1970's, so Python's way of doing things fits my brain quite nicely,
thank you.


So, people can probably say that about any language they started with.
That a language suits a certain persons brain, may say more about
the person than about the language.

--
Antoon Pardon
Nov 4 '05 #65
Antoon Pardon wrote:
Because b.a += 2 expands to b.a = b.a + 2. Why would you want b.a =
<something> to correspond to b.__class__.a = <something>?

That is an implemantation detail. The only answer that you are given
means nothing more than: because it is implemented that way.


Something that is written in the language reference is not
an implementation detail. Every implementation that aims to
be Python must follow this. It's a design decision.

Whether you like it or not, you will find out that the behaviour
of Python is largely based on an idea of an underlying structure.
A lot of the syntax is basically just convenient ways to access
this structure, and there is a strong tradition to avoid magic.

The explicit use of self might be the most obvious example of that,
but you can find a lot of other things in Python that shows you
this, __dict__ for instance.

I agree that the behaviour you are questioning isn't completely
unsurprising for someone who stumbles over it the first time, but
considering how things work in Python classes, where the class
scope is searched if a name isn't found in the instance scope
(self.__dict__), any other solution would involve more magic, and
be more surprising to someone who actually knows what is going on.

It's possible that a oldie like me, who started coding Python in
1996 is just blind to the warts in Python by now, but no language
is perfect, and whatever design decisions you make, they will have
both positive and negative consequences.

I frankly don't understand what you are after Antoon. Just to
vent your frustrations? If you want to use Python in an effective
way, try to learn how to use the language that actually exists.

Asking questions in this forum is clearly a part of that, but
your confrontational style, and idea that everything that bothers
you is a language bug that needs to be fixed is not the most
constructive approach. I'm pretty sure that it doesn't really solve
your coding problems, instead it leads the discussion away from the
practical solutions.

If you really want to improve the Python language, your approach
is completely off target. First of all, this isn't really the right
forum for that, and secondly, improvements to Python requires a
lot of cooperation and substantial contributions of work, not just
complaints, even if you might have a point now and then.
Nov 4 '05 #66
Antoon Pardon <ap*****@forel.vub.ac.be> writes:

....
Would it be too much to ask that in a line like.

x = x + 1.

both x's would resolve to the same namespace?


This is starting to look more like a nagging contest than a real
discussion imo.

Consider changing the semantics of what you are proposing and
think about all those Python projects that will break because they
depend on the above behaviour and even take advantage of it.

So in short: yes, it would be too much to ask :-)

But, you can fix it in your own code. Simply make sure that your
class variables have different names or a prefix.

S.
Nov 4 '05 #67
Antoon Pardon <ap*****@forel.vub.ac.be> writes:
Op 2005-11-03, Mike Meyer schreef <mw*@mired.org>:
Antoon Pardon <ap*****@forel.vub.ac.be> writes:
What would you expect to get if you wrote b.a = b.a + 2?
I would expect a result consistent with the fact that both times
b.a would refer to the same object.


Except they *don't*. This happens in any language that resolves
references at run time.


Python doesn't resolve references at run time. If it did the following
should work.

a = 1
def f():
a = a + 1

f()


No that has nothing to do with resolving things at runtime. Your example
does not work because the language is very specific about looking up
global variables. Your programming error, not Python's shortcoming.

S.
Nov 4 '05 #68
Op 2005-11-04, Stefan Arentz schreef <st***********@gmail.com>:
Antoon Pardon <ap*****@forel.vub.ac.be> writes:

...
> Ah yes. Well, good luck with that. You seem to have decided that it is not
> sane and who am I to argue with that. It depends on your state of mind :-)
I can just say the opposite, that you seem to have decided that it is
sane.


I have. I like the Python model.


Fine good for you.
> The model makes sense in my opinion. If you don't like it then there are
> plenty of other languages to choose from that have decided to implement
> things differently.


And again this argument. Like it or leave it, as if one can't in general
like the language, without being blind for a number of shortcomings.


Personally I don't see it as a shortcoming.


Which isn't the point.
It is this kind of recations that make me think a number of people is
blindly devoted to the language to the point that any criticism of
the language becomes intollerable.


No not at all. Just look at all the PEPs and the changes in the language
that have been made in the past. Python is very much community driven and
that shows in it's progress.

You on the other hand keep talking about emo things like 'sane' and
'madness' without giving any technical backing about these problems
that you are having with the language.


That you took those emotionally, is not my responsibility. Would you
prefered it, had I called it a wart? As far as I see it, any negative
comment on python is reacted to in pretty much the same way. So
don't blame it on me using emotional language.

--
Antoon Pardon
Nov 4 '05 #69
Stefan Arentz <st***********@gmail.com> writes:
Would it be too much to ask that in a line like.
x = x + 1.
both x's would resolve to the same namespace?

...
Consider changing the semantics of what you are proposing and
think about all those Python projects that will break because they
depend on the above behaviour and even take advantage of it.


Are you seriously saying there's lots of Python projects that would
break if this particular weirdness were fixed?
Nov 4 '05 #70
Paul Rubin <http://ph****@NOSPAM.invalid> writes:
Stefan Arentz <st***********@gmail.com> writes:
Would it be too much to ask that in a line like.
x = x + 1.
both x's would resolve to the same namespace?

...
Consider changing the semantics of what you are proposing and
think about all those Python projects that will break because they
depend on the above behaviour and even take advantage of it.


Are you seriously saying there's lots of Python projects that would
break if this particular weirdness were fixed?


I have no numbers of course. But, why is this a weirdness?

S.
Nov 4 '05 #71
Antoon Pardon <ap*****@forel.vub.ac.be> writes:
Op 2005-11-03, Mike Meyer schreef <mw*@mired.org>:
Antoon Pardon <ap*****@forel.vub.ac.be> writes:
What would you expect to get if you wrote b.a = b.a + 2?
I would expect a result consistent with the fact that both times
b.a would refer to the same object. Except they *don't*. This happens in any language that resolves
references at run time.

Python doesn't resolve references at run time. If it did the following
should work.


You left out a key word: "all".
a = 1
def f():
a = a + 1

f()
If Python didn't resolve references at run time, the following
wouldn't work:
def f(): .... global a
.... a = a + 1
.... a = 1
f()
But letting that aside. There is still a difference between resolving
reference at run time and having the same reference resolved twice
with each resolution a different result.


The second is a direct result of the first. The environment can change
between the references, so they resolve to different results.


Changing that would be changing a fundamental
- and *important* - feature of Python. Arbitrary restrictions to
prevent a single case of this from doing something people who aren't
used to suvh behavior are kludges, and would constitute a wart on the
language, pure and simple.

Python already has its warts. If you want to argue that fixing this
would make a bigger wart then the wart it is now. Fine I will accept
that.


I've already argued that the kludges suggested to "solve" this problem
create worse problems than this. This is a simple case of something
being unexpected to those used to less dynamic languages. The other
solutions break useful functionality, and require adding special cases
to the language - which aren't special enough to break the rules.

<mike
--
Mike Meyer <mw*@mired.org> http://www.mired.org/home/mwm/
Independent WWW/Perforce/FreeBSD/Unix consultant, email for more information.
Nov 4 '05 #72
Would it be too much to ask that in a line like.

x = x + 1.

both x's would resolve to the same namespace?


Yes. That's to much bondage for programmers who've become accustomed
to freedom. Explain why this should be illegal:
class C: .... def __getattr__(self, name):
.... x = 1
.... return locals()[name]
.... def __setattr__(self, name, value):
.... globals()[name] = value
.... o = C()
o.x = o.x + 1
x 2

<mike
--
Mike Meyer <mw*@mired.org> http://www.mired.org/home/mwm/
Independent WWW/Perforce/FreeBSD/Unix consultant, email for more information.
Nov 4 '05 #73
Stefan Arentz <st***********@gmail.com> writes:
Are you seriously saying there's lots of Python projects that would
break if this particular weirdness were fixed?


I have no numbers of course. But, why is this a weirdness?


Do you seriously think the number is larger than zero? Do you think
that's any good way to write code?

Examples of the weirdness have already been given. My favorite is the
one where b.a is a list instead of an integer, in which case the class
variable gets updated instead of an instance variable getting created.
If you don't find the inconsistency to be weird, then ducky for you.
Nov 4 '05 #74
Mike Meyer <mw*@mired.org> writes:
I've already argued that the kludges suggested to "solve" this problem
create worse problems than this.


The most obvious solution is to permit (or even require) the
programmer to list the instance variables as part of the class
definition. Anything not in the list is not an instance variable,
i.e. they don't get created dynamically. That's what most other
languages I can think of do. Some Python users incorrectly think this
is what __slots__ does, and try to use __slots__ that way. That they
try to do that suggests that the approach makes some sense.
Nov 4 '05 #75
Op 2005-11-04, Magnus Lycka schreef <ly***@carmen.se>:
Antoon Pardon wrote:
Because b.a += 2 expands to b.a = b.a + 2. Why would you want b.a =
<something> to correspond to b.__class__.a = <something>?

That is an implemantation detail. The only answer that you are given
means nothing more than: because it is implemented that way.


Something that is written in the language reference is not
an implementation detail. Every implementation that aims to
be Python must follow this. It's a design decision.


I have looked and didn't find it in the language reference.

This is what I have found:

An augmented assignment expression like x += 1 can be rewritten
as x = x + 1 to achieve a similar, but not exactly equal effect.

I think one could argue that in the case of b.a += 1 and a
being a class variable that incrementing the class variable
was a similar effect in this case.

But I can be and maybe a more strict definition is available
that I looked over. Do happen to know one?
Whether you like it or not, you will find out that the behaviour
of Python is largely based on an idea of an underlying structure.
A lot of the syntax is basically just convenient ways to access
this structure, and there is a strong tradition to avoid magic.
Fine. I already wrote that if people think that changing this
behaviour would cause more problems than it solved or that
solving it would cause more problems than it is worth, I would
have no problem with that.

That doesn't change the fact that the current behaviour is
on occasions awkward or whatever you want to call it.
The explicit use of self might be the most obvious example of that,
but you can find a lot of other things in Python that shows you
this, __dict__ for instance.

I agree that the behaviour you are questioning isn't completely
unsurprising for someone who stumbles over it the first time, but
considering how things work in Python classes, where the class
scope is searched if a name isn't found in the instance scope
(self.__dict__), any other solution would involve more magic, and
be more surprising to someone who actually knows what is going on.
It would be more suprising to someone depending on what is now
going on. I also find that people underestimate the magic that
is going on in python. But just because you are familiar with
the magic, doesn't make it less magic. IMO python shows its
history a little.
It's possible that a oldie like me, who started coding Python in
1996 is just blind to the warts in Python by now, but no language
is perfect, and whatever design decisions you make, they will have
both positive and negative consequences.
I completely agree. Personnaly I find python has withstood
its changes remarkebly well and I find the design in general
still very consistent despite the changes it underwent.
I frankly don't understand what you are after Antoon. Just to
vent your frustrations? If you want to use Python in an effective
way, try to learn how to use the language that actually exists.
I'm after nothing particular. The only thing I'm frustrated about
is the way in which some people seem willing to defend python
just because it is python. If the only reaction I would have
gotten would have been something like: Yeah that seems a bit
awkward but fixing this would break more than it would cure,
I would have left it as it is.
Asking questions in this forum is clearly a part of that, but
your confrontational style, and idea that everything that bothers
you is a language bug that needs to be fixed is not the most
constructive approach.


I have rarely indicated I wanted things to be fixed. Sure I would
like it if some things were different, but I recognize that there
are more important things that needs to be resolved.

Does that mean I shouldn't mention things that IMO could have been
better or that I should only mention them in the softest of
language that certainly can't be interpreted as emotional language.

--
Antoon pardon
Nov 4 '05 #76
Op 2005-11-04, Mike Meyer schreef <mw*@mired.org>:
Would it be too much to ask that in a line like.

x = x + 1.

both x's would resolve to the same namespace?


Yes. That's to much bondage for programmers who've become accustomed
to freedom. Explain why this should be illegal:
class C: ... def __getattr__(self, name):
... x = 1
... return locals()[name]
... def __setattr__(self, name, value):
... globals()[name] = value
... o = C()
o.x = o.x + 1
x

2


I'll answer with a contra question.

Please explain why this is illegal.

x = 1
def f():
x += 1

f()

IMO your example and mine are essentially the same issue. A name in one
namespace shadowing a name in a different namespace.

So please explain how the same kind of bondage is no problem in the
function but is too much for those who've become accustomed to
freedom in the case of objects with class variables?

--
Antoon Pardon
Nov 4 '05 #77
Op 2005-11-04, Stefan Arentz schreef <st***********@gmail.com>:
Antoon Pardon <ap*****@forel.vub.ac.be> writes:
Op 2005-11-03, Mike Meyer schreef <mw*@mired.org>:
> Antoon Pardon <ap*****@forel.vub.ac.be> writes:
>>> What would you expect to get if you wrote b.a = b.a + 2?
>> I would expect a result consistent with the fact that both times
>> b.a would refer to the same object.
>
> Except they *don't*. This happens in any language that resolves
> references at run time.


Python doesn't resolve references at run time. If it did the following
should work.

a = 1
def f():
a = a + 1

f()


No that has nothing to do with resolving things at runtime. Your example
does not work because the language is very specific about looking up
global variables. Your programming error, not Python's shortcoming.


It has nothing to do with global variables, the same thing happens
with nested scopes.

def f():
a = 1
def g():
a = a + 1

g()

f()
Nov 4 '05 #78
Op 2005-11-04, Mike Meyer schreef <mw*@mired.org>:
Antoon Pardon <ap*****@forel.vub.ac.be> writes:
Op 2005-11-03, Mike Meyer schreef <mw*@mired.org>:
Antoon Pardon <ap*****@forel.vub.ac.be> writes:
> What would you expect to get if you wrote b.a = b.a + 2?
I would expect a result consistent with the fact that both times
b.a would refer to the same object.
Except they *don't*. This happens in any language that resolves
references at run time.

Python doesn't resolve references at run time. If it did the following
should work.


You left out a key word: "all".
a = 1
def f():
a = a + 1

f()


If Python didn't resolve references at run time, the following
wouldn't work:
def f(): ... global a
... a = a + 1
... a = 1
f()

Why do you think so? I see nothing here that couldn't work with
a reference resolved during compile time.
But letting that aside. There is still a difference between resolving
reference at run time and having the same reference resolved twice
with each resolution a different result.


The second is a direct result of the first. The environment can change
between the references, so they resolve to different results.


No the second is not a direct result of the first. Since there is
only one reference, I see nothing wrong with the environment
remebering the reference and reusing it if it needs the reference
a second time.

Take the code:

lst[f()] += 1

Now let f be a function with a side effect, that in succession
produces the positive integers starting with one.

What do you think this should be equivallent to:

t = f()
lst[t] = lst[t] + 1

or

lst[f()] = lst[f()] + 1

If you think the environment can change between references then I
suppose you prefer the second approach.

--
Antoon Pardon
Nov 4 '05 #79
Op 2005-11-04, Graham schreef <gr***********@gmail.com>:
Once again, many thanks, your explainations are very detailed and i
think i'm in full understanding of the what/when/why of it all.

And with further introspection i can see why its done this way from a
language processing point of view rather than programming one. I also
now realize that <instance>.<classvarname> is there so that you dont
have to type <instance>.__class__.<varname> all the time.


You still have to if you want to change the class variable.

--
Antoon Pardon
Nov 4 '05 #80
On Fri, 04 Nov 2005 07:31:46 +0000, Antoon Pardon wrote:
The model makes sense in my opinion. If you don't like it then there are
plenty of other languages to choose from that have decided to implement
things differently.


And again this argument. Like it or leave it, as if one can't in general
like the language, without being blind for a number of shortcomings.

It is this kind of recations that make me think a number of people is
blindly devoted to the language to the point that any criticism of
the language becomes intollerable.


There are good usage cases for the current inheritance behaviour. I asked
before what usage case or cases you have for your desired behaviour, and
you haven't answered. Perhaps you missed the question? Perhaps you haven't
had a chance to reply yet? Or perhaps you have no usage case for the
behaviour you want.

Some things are a matter of taste: should CPython prefer <> or != for not
equal? Some things are a matter of objective fact: should CPython use a
byte-code compiler and virtual machine, or a 1970s style interpreter that
interprets the source code directly?

The behaviour you are calling "insane" is partly a matter of taste, but it
is mostly a matter of objective fact. I believe that the standard
model for inheritance that you call insane is rational because it is
useful in far more potential and actual pieces of code than the behaviour
you prefer -- and the designers of (almost?) all OO languages seem to
agree with me.

The standard behaviour makes it easy for code to do the right thing in
more cases, without the developer taking any special steps, and in the
few cases where it doesn't do the right thing (e.g. when the behaviour you
want is for all instances to share state) it is easy to work around. By
contrast, the behaviour you want seems to be of very limited usefulness,
and it makes it difficult to do the expected thing in almost all cases,
and work-arounds are complex and easy to get wrong.

The standard behaviour makes it easy for objects to inherit state, and
easy for them to over-ride defaults. The behaviour(s) you and Graham want
have awkward side-effects: your proposed behaviour would mean that class
attributes would mask instance attributes, or vice versa, meaning that
the programmer would have to jump through hoops to get common types of
behaviour like inheriting state.

The behaviour you want would make it too easy to inadvertently have
instances share state. Normally we want instances to share behaviour but
have unique states -- you would change that. Why? If it is just a matter
of taste, you are welcome to your opinion. But you don't say that the
standard behaviour is "ugly", you say it is "insane", that is, irrational,
and that the behaviour you want is rational.

That's an objective claim: please explain what makes your behaviour more
rational than the standard behaviour. Is your behaviour more useful? Does
it make code easier to write? Does it result in more compact code? What
usage cases?

Or is it just a subjective judgement on your part that it would be neater?
--
Steven.

Nov 4 '05 #81
Paul Rubin wrote:
Stefan Arentz <st***********@gmail.com> writes:
Are you seriously saying there's lots of Python projects that would
break if this particular weirdness were fixed?
I have no numbers of course. But, why is this a weirdness?

Do you seriously think the number is larger than zero? Do you think
that's any good way to write code?

Well it would break the Medusa asyncore/asynchat-based server software,
so I can confidently predict the number would be greater than zero, yes.

Several fine programmers have relied on the (documented) behavior, I
suspect, as it's a convenient way to install per-instance defaults, for
example.
Examples of the weirdness have already been given. My favorite is the
one where b.a is a list instead of an integer, in which case the class
variable gets updated instead of an instance variable getting created.
If you don't find the inconsistency to be weird, then ducky for you.


Ho, hum.
--
Steve Holden +44 150 684 7255 +1 800 494 3119
Holden Web LLC www.holdenweb.com
PyCon TX 2006 www.python.org/pycon/

Nov 4 '05 #82
Antoon Pardon wrote:
Op 2005-11-04, Steve Holden schreef <st***@holdenweb.com>: [...]
I suppose ultimately I'm just more pragmatic than you.

It has nothing to do with being more pragmatic. Being pragmatic
is about how you handle things with real life projects. It has
little to do with the degree in which you agree with the design
of the tool you have to work with. I would say I am more pragmatic
than most defenders of python, because when it comes done to
do my work, I just use python as best as I can, while a lot
of people here seem to think that every little criticism I have
is enough to go and look for a different language.

No, being pragmatic is to do with accepting what is rather than wasting
time wishing it were otherwise, particularly when the "insane" behavior
was actually a deliberate design choice. Which is why it doesn't work
the same as non-local references in nested scopes.
Plus I started
using Icon, whose assignment semantics are very similar, back in the
1970's, so Python's way of doing things fits my brain quite nicely,
thank you.

So, people can probably say that about any language they started with.
That a language suits a certain persons brain, may say more about
the person than about the language.

I wasn't trying to make a point about the language. I was merely (and I
thought charitably) trying to explain why I appear to be blind to the
"insanity" you see all around you.

regards
Steve
--
Steve Holden +44 150 684 7255 +1 800 494 3119
Holden Web LLC www.holdenweb.com
PyCon TX 2006 www.python.org/pycon/

Nov 4 '05 #83
Op 2005-11-04, Steven D'Aprano schreef <st***@REMOVETHIScyber.com.au>:
On Fri, 04 Nov 2005 07:31:46 +0000, Antoon Pardon wrote:
The model makes sense in my opinion. If you don't like it then there are
plenty of other languages to choose from that have decided to implement
things differently.
And again this argument. Like it or leave it, as if one can't in general
like the language, without being blind for a number of shortcomings.

It is this kind of recations that make me think a number of people is
blindly devoted to the language to the point that any criticism of
the language becomes intollerable.


There are good usage cases for the current inheritance behaviour. I asked
before what usage case or cases you have for your desired behaviour, and
you haven't answered. Perhaps you missed the question? Perhaps you haven't
had a chance to reply yet? Or perhaps you have no usage case for the
behaviour you want.


There are good use cases for a lot of things python doesn't provide.
There are good use cases for writable closures, but python doesn't
provide it, shrug, I can live with that. Use cases is a red herring
here.
Some things are a matter of taste: should CPython prefer <> or != for not
equal? Some things are a matter of objective fact: should CPython use a
byte-code compiler and virtual machine, or a 1970s style interpreter that
interprets the source code directly?

The behaviour you are calling "insane" is partly a matter of taste, but it
is mostly a matter of objective fact. I believe that the standard
model for inheritance that you call insane is rational because it is
useful in far more potential and actual pieces of code than the behaviour
you prefer -- and the designers of (almost?) all OO languages seem to
agree with me.
I didn't call the model for inheritance insane.
The standard behaviour makes it easy for code to do the right thing in
more cases, without the developer taking any special steps, and in the
few cases where it doesn't do the right thing (e.g. when the behaviour you
want is for all instances to share state) it is easy to work around. By
contrast, the behaviour you want seems to be of very limited usefulness,
and it makes it difficult to do the expected thing in almost all cases,
and work-arounds are complex and easy to get wrong.
Please don't make this about what I *want*. I don't want anything.
I just noted that one and the same reference can be processed
multiple times by the python machinery, resulting in that same
reference referencing differnt variables at the same time and
stated that that was unsane behaviour.
The standard behaviour makes it easy for objects to inherit state, and
easy for them to over-ride defaults. The behaviour(s) you and Graham want
have awkward side-effects: your proposed behaviour would mean that class
attributes would mask instance attributes, or vice versa, meaning that
the programmer would have to jump through hoops to get common types of
behaviour like inheriting state.
You don't know what I want. You only know that I have my criticism of
particular behaviour. You seem to have your idea about what the
alternative would be like, and project that to what I would want.
The behaviour you want would make it too easy to inadvertently have
instances share state. Normally we want instances to share behaviour but
have unique states -- you would change that. Why? If it is just a matter
of taste, you are welcome to your opinion. But you don't say that the
standard behaviour is "ugly", you say it is "insane", that is, irrational,
and that the behaviour you want is rational.
I called it unsane, not insane. I think I paid enough attention to never
use the word "insane", yes I once used "madness" but that was after you
were already all over me for this.

Even should I have used the word insane, I used it for a lot less than
you are implying.
That's an objective claim: please explain what makes your behaviour more
rational than the standard behaviour. Is your behaviour more useful? Does
it make code easier to write? Does it result in more compact code? What
usage cases?


What my behaviour? I don't need to specify alternative behaviour in
order to judge specific behaviour.

--
Antoon Pardon
Nov 4 '05 #84
Op 2005-11-04, Steve Holden schreef <st***@holdenweb.com>:
Antoon Pardon wrote:
Op 2005-11-04, Steve Holden schreef <st***@holdenweb.com>: [...]
I suppose ultimately I'm just more pragmatic than you.

It has nothing to do with being more pragmatic. Being pragmatic
is about how you handle things with real life projects. It has
little to do with the degree in which you agree with the design
of the tool you have to work with. I would say I am more pragmatic
than most defenders of python, because when it comes done to
do my work, I just use python as best as I can, while a lot
of people here seem to think that every little criticism I have
is enough to go and look for a different language.

No, being pragmatic is to do with accepting what is rather than wasting
time wishing it were otherwise,


Just accepting what is, is not pragmatic. Not much progress would have
been made if we just accepted what is.
particularly when the "insane" behavior
was actually a deliberate design choice. Which is why it doesn't work
the same as non-local references in nested scopes.


That b.a = b.a + 2

works as a result of a design choice, that I can accept.

But IMO b.a += 2, working as it does, is more the result of
earlier design and implementation decisions than it was
a deliberate design decision.

--
Antoon Pardon
Nov 4 '05 #85
Steven D'Aprano <st***@REMOVETHIScyber.com.au> writes:
There are good usage cases for the current inheritance behaviour.


Can you name one? Any code that relies on it seems extremely dangerous to me.
Nov 4 '05 #86
Antoon Pardon wrote:
Would it be too much to ask that in a line like.

x = x + 1.

both x's would resolve to the same namespace?


They always do Antoon. There is no such issue for
local (or global) varibles. The issue has to do
with c.x = c.x + 1. In this case it's clearly
designed and documented that this corresponds to:

setattr(c, 'x', getattr(c, 'x') + 1)

The result of these operations depends on e.g.
how the __setattr__ and __getattr__ methods in
the class in question are defined.

You need to understand that the dot-operaterator
always involves a lookup-operation that can be
implemented in various ways.

It's well defined that you can do things like:
class Counter: .... c=0
.... def __call__(self):
.... self.c+=1
.... def __str__(self):
.... return str(self.c)
.... c=Counter()
c()
print c 1 c()
print c 2 class C5(Counter): .... c=5
.... c5=C5()
c5()
print c5 6

Of course, you could design a language, say Pythoon
or Parthon, where this is illegal, and you force the
programmer to do something longer such as:
class APCounter:

.... c=0
.... def __init__(self):
.... self.c = self.__class__.c
.... def __call__(self):
.... self.c+=1
.... def __str__(self):
.... return str(self.c)
....

I don't see this as an improvement though...
Nov 4 '05 #87
Op 2005-11-04, Magnus Lycka schreef <ly***@carmen.se>:
Antoon Pardon wrote:
Would it be too much to ask that in a line like.

x = x + 1.

both x's would resolve to the same namespace?
They always do Antoon. There is no such issue for
local (or global) varibles.


I meant those 'x' do be any general expression
that refers to an object. Like

a.b[c.f] = a.b[c.f] + 1
The issue has to do
with c.x = c.x + 1. In this case it's clearly
designed and documented that this corresponds to:

setattr(c, 'x', getattr(c, 'x') + 1)
The issue is with c.x += 1

Sure I find the fact that the same reference two
times in the same line can reference variable
in two different namespaces ugly.

But that one single reference refers to two
variables in two different namespaces that is
IMO more than ugly.

Suppose I have the following:

class I:
def __init__(self):
self.v = 0
def __call__(self):
t = self.v
self.v += 1
return t

i = I()
lst = range(10)
lst[i()] += 20

Nobody seems to find that this should be treated
exactly the same as
lst[i()] = lst[i()] + 20

People seem to think since lst[i()] only occurs
once, it should be only refering to one entity.

Well I think the same kind of reasoning can apply
to c.x += 1.
The result of these operations depends on e.g.
how the __setattr__ and __getattr__ methods in
the class in question are defined.

You need to understand that the dot-operaterator
always involves a lookup-operation that can be
implemented in various ways.
But there is no reason that two dot-operator are
executed when only one dot-operator is in the text.

Just as there is no reason that two i() calls
should be made when only one call is in the text.
It's well defined that you can do things like:
class Counter: ... c=0
... def __call__(self):
... self.c+=1
... def __str__(self):
... return str(self.c)
... c=Counter()
c()
print c 1 c()
print c 2 class C5(Counter): ... c=5
... c5=C5()
c5()
print c5 6

Of course, you could design a language, say Pythoon
or Parthon, where this is illegal, and you force the
programmer to do something longer such as:
class APCounter:

... c=0
... def __init__(self):
... self.c = self.__class__.c
... def __call__(self):
... self.c+=1
... def __str__(self):
... return str(self.c)
...

I don't see this as an improvement though...


Well I thought in python explicit was better than
implicit.

--
Antoon Pardon
Nov 4 '05 #88
Antoon Pardon wrote:
Op 2005-11-03, Stefan Arentz schreef <st***********@gmail.com>:
The model makes sense in my opinion. If you don't like it then there are
plenty of other languages to choose from that have decided to implement
things differently.

And again this argument. Like it or leave it, as if one can't in general
like the language, without being blind for a number of shortcomings.

It is this kind of recations that make me think a number of people is
blindly devoted to the language to the point that any criticism of
the language becomes intollerable.


No, it's just that a goodly number of people actually -like- the
relatively simple conceputal model of Python.

Why /shouldn't/
a.x = foo
correspond exactly to
setattr(a,'x',foo) #?
Similarly, why shouldn't
foo = a.x
correspond exactly to
foo = getattr(a,'x') #?
With that in mind, the logical action for
a.x = f(a.x)
is
setattr(a,'x',f(a,'x')) #,
and since
a.x += foo
is equal to
a.x = A.__iadd__(a.x,foo) # (at least for new-style classes
# that have __iadd__ defined. Otherwise, it falls back on
# __add__(self,other) to return a new object, making this
# evern more clear),
why shouldn't this translate into
setattr(a,'x',A.__iadd__(getattr(a,'x'),foo)) #?
Looking at it this way, it's obvious that the setattr and getattr may do
different things, if the programmer understands that "instances (can)
look up object attributes, and (always) set instance attributes." In
fact, it is always the case (so far as I can quickly check) that += ends
up setting an instance attribute. Consider this code:
class foo:
x = [5]
a = foo()
a += [6]
a.x [5,6] foo.x [5,6] foo.x = [7]
a.x

[5,6]

In truth, this all does make perfect sense -- if you consider class
variables mostly good for "setting defaults" on instances.
Nov 4 '05 #89
Steven D'Aprano wrote:
On Thu, 03 Nov 2005 14:13:13 +0000, Antoon Pardon wrote:

Fine, we have the code:

b.a += 2

We found the class variable, because there is no instance variable,
then why is the class variable not incremented by two now?

Because b.a += 2 expands to b.a = b.a + 2. Why would you want b.a =
<something> to correspond to b.__class__.a = <something>?


Small correction, it expands to b.a = B.a.__class__.__iadd__(b.a,2),
assuming all relevant quantities are defined. For integers, you're
perfectly right.
Nov 4 '05 #90
Op 2005-11-04, Christopher Subich schreef <cs****************@spam.subich.block.com>:
Antoon Pardon wrote:
Op 2005-11-03, Stefan Arentz schreef <st***********@gmail.com>:
The model makes sense in my opinion. If you don't like it then there are
plenty of other languages to choose from that have decided to implement
things differently.

And again this argument. Like it or leave it, as if one can't in general
like the language, without being blind for a number of shortcomings.

It is this kind of recations that make me think a number of people is
blindly devoted to the language to the point that any criticism of
the language becomes intollerable.


No, it's just that a goodly number of people actually -like- the
relatively simple conceputal model of Python.

Why /shouldn't/
a.x = foo
correspond exactly to
setattr(a,'x',foo) #?
Similarly, why shouldn't
foo = a.x
correspond exactly to
foo = getattr(a,'x') #?
With that in mind, the logical action for
a.x = f(a.x)
is
setattr(a,'x',f(a,'x')) #,
and since
a.x += foo
is equal to
a.x = A.__iadd__(a.x,foo) # (at least for new-style classes
# that have __iadd__ defined. Otherwise, it falls back on
# __add__(self,other) to return a new object, making this
# evern more clear),
why shouldn't this translate into
setattr(a,'x',A.__iadd__(getattr(a,'x'),foo)) #?


Well maybe because as far as I understand the same kind of logic
can be applied to something like

lst[f()] += foo

In order to decide that this should be equivallent to

lst[f()] = lst[f()] + foo.

But that isn't the case.

So it seems applying augmented operators is not a matter of just
substituting straight translations to get the right result.

Looking at it this way, it's obvious that the setattr and getattr may do
different things, if the programmer understands that "instances (can)
look up object attributes, and (always) set instance attributes." In
fact, it is always the case (so far as I can quickly check) that += ends
up setting an instance attribute. Consider this code:


Looking at lists in a similar way, it would be obvious that the
__setitem__ and __getitem__ can do different things and so we
should expect lst[f()] += foo to behave exactly as lst[f()] = lst[f()] +
foo.

--
Antoon Pardon
Nov 4 '05 #91
Op 2005-11-04, Christopher Subich schreef <cs****************@spam.subich.block.com>:
Antoon Pardon wrote:
Op 2005-11-03, Stefan Arentz schreef <st***********@gmail.com>:
The model makes sense in my opinion. If you don't like it then there are
plenty of other languages to choose from that have decided to implement
things differently.

class foo:
x = [5]
a = foo()
a += [6]
a.x [5,6] foo.x [5,6] foo.x = [7]
a.x

[5,6]

In truth, this all does make perfect sense -- if you consider class
variables mostly good for "setting defaults" on instances.


Except when your default is a list

class foo:
x = [] # default

a = foo()
a.x += [3]

b = foo()
b.x

This results in [3]. So in this case using a class variable x to
provide a default empty list doesn't work out in combination
with augmented operators.

This however would work:

class foo:
x = [] # default

a = foo()
a.x = a.x + [3]

b = foo()
b.x

This results in []

--
Antoon Pardon
Nov 4 '05 #92
On Fri, 04 Nov 2005 09:03:56 +0000, Antoon Pardon wrote:
Op 2005-11-03, Steven D'Aprano schreef <st***@REMOVETHIScyber.com.au>:
On Thu, 03 Nov 2005 13:01:40 +0000, Antoon Pardon wrote:
Seems perfectly sane to me.

What would you expect to get if you wrote b.a = b.a + 2?

I would expect a result consistent with the fact that both times
b.a would refer to the same object.


class RedList(list):
colour = "red"

L = RedList(())

What behaviour would you expect from len(L), given that L doesn't have a
__len__ attribute?


Since AFAICT there is no single reference to the __len__ attribute that
will be resolved to two different namespace I don't see the relevance.


Compare:

b.a += 2

Before the assignment, instance b does not have an attribute "a", so class
attribute "a" is accessed. You seem to be objecting to this inheritance.

len(L) => L.__len__()

Instance L also does not have an attribute "__len__", so class attribute
"__len__" is accessed. You don't appear to object to this inheritance.

Why object to one and not the other?

If you object to b.a resolving to b.__class__.a, why don't you object to
L.__len__ resolving to L.__class__.__len__ also?

Perhaps you don't object to that half of the problem. Perhaps you object
to the assignment: you expect that assigning to b.a should assign to
b.__class__.a instead.

Should assigning to L[0] assign to L.__class__[0] also, so that all
lists share not only the same behaviour, but also the same data?
[snip]
b is a name, and any reference to b (in the same namespace) will refer
to the same object. At least until you rebind it to another object.


But some namespaces take great care not to allow a rebinding that would
result in the same name being resolved to a different namespace during
this namespace's lifetime.


And some take great care to allow such a rebinding, because that is the
right thing to do to make inheritance work correctly.

But b.a is not a name, it is an attribute lookup,


An other implementation detail. b.a is a name search of 'a' in the
namespace b.


Factually incorrect. b.a is the name search for 'a' in the namespaces
[note plural] of b, b.__class__, and any superclasses of b, *in that order*.

Do you object to import searching multiple directories?

Why do you object to attribute resolution searching multiple namespaces?
[snip]
I think it even less sane, if the same occurce of b.a refers to two
different objects, like in b.a += 2


Then it seems to me you have some serious design problems. Which would
you prefer to happen?

# Scenario 1
# imaginary pseudo-Python code with no inheritance: class Paragraph:
ls = '\n' # line separator

para = Paragraph()
para.ls

=> AttributeError - instance has no attribute 'ls'
# Scenario 2
# imaginary pseudo-Python code with special inheritance:
class Paragraph:
ls = '\n' # line separator

linux_para = Paragraph()
windows_para = Paragraph()
windows_para.ls = '\n\r' # magically assigns to the class attribute
linux_para.ls

=> prints '\n\r'

# Scenario 3
# Python code with standard inheritance:
class Paragraph:
ls = '\n' # line separator

linux_para = Paragraph()
windows_para = Paragraph()
windows_para.ls = '\n\r'
linux_para.ls

=> prints '\n'

I don't see the relevance of these pieces of code. In none of them is
there an occurence of an attribute lookup of the same attribute that
resolves to different namespaces.


Look a little more closely. In all three pieces of code, you have a
conflict between the class attribute 'ls' and an instance attribute 'ls'.

In the first scenario, that conflict is resolved by insisting that
instances explicitly define an attribute, in other words, by making
instance attribute ONLY search the instance namespace and not the class
namespace.

In the second scenario, that conflict is resolved by insisting that
instance.name assigns to instance.__class__.name, just as you asked for.

The third scenario is the way Python actually operates.

--
Steven.

Nov 4 '05 #93
Antoon Pardon wrote:
Since ints are immutable objects, you shouldn't expect the value of b.a
to be modified in place, and so there is an assignment to b.a, not A.a.

You are now talking implementation details. I don't care about whatever
explanation you give in terms of implementation details. I don't think
it is sane that in a language multiple occurence of something like b.a
in the same line can refer to different objects


This isn't an implementation detail; to leading order, anything that
impacts the values of objects attached to names is a specification issue.

An implementation detail is something like when garbage collection
actually happens; what happens to:

b.a += 2

is very much within the language specification. Indeed, the language
specification dictates that an instance variable b.a is created if one
didn't exist before; this is true no matter if type(b.a) == int, or if
b.a is some esoteric mutable object that just happens to define
__iadd__(self,type(other) == int).
Nov 4 '05 #94
Antoon Pardon wrote:
Well I wonder. Would the following code be considered a name binding
operation:

b.a = 5


Try it, it's not.

Python 2.2.3 (#1, Nov 12 2004, 13:02:04)
[GCC 3.2.3 20030502 (Red Hat Linux 3.2.3-42)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
a Traceback (most recent call last):
File "<stdin>", line 1, in ?
NameError: name 'a' is not defined b = object()
b.a

Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: 'object' object has no attribute 'a'

Once it's attached to an object, it's an attribute, not a base name.
The distinction is subtle and possibly something that could (should?) be
unified for Py3k, but in cases like this the distinction is important.
Nov 4 '05 #95
Graham wrote:
My question remains however, i suppose i'm not familiar with how this
functions in
other languages, but what is the common way of referring to a class
variable.

is <class>.<var> the norm?
or <instance>.<var> the norm.
It's not always that easy, due to inheritance. You might want
the <var> defined in the class where a method you define now
is implemented (A.<var> if we're in a method defined in class A),
or you might want <var> in the class of the instance object
(which could be a subclass of A). You can get that through
self.__class__.<var>, so I guess you could always manage without
Python searching in the class scope after searching the instance
scope, if it wasn't for the problem below...
I just seems to me that <instance>.<var> shouldn't defer to the class
variable if
an instance variable of the same name does not exists, it should, at
least how i
understand it raise an exception.


So, you want this:
class A: .... def f(self):
.... print 'Hello'
.... a = A()
a.f() Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: A instance has no attribute 'f'

A bit boring that we need to make method calls
with a.__class__.f(a) in your Python...

Python is more consistent than you have thought...
You know, it could be that we want to assign the
method to another variable, as in:

o = A()
o_f = o.f # This might look as I'm getting a normal
# attribute, but f is a method
for i in range(gazillion):
o_f() # This is slightly faster than o.f()

Or, we might want to do:
o.f=len
o.f('Hello')

Here, o.f is no longer a method in o's class hierarchy, but
it's still callable.

If you think about it, you'll understand that in such a dynamic
language as Python, there is no way that the interpreter can
know before lookup whether it will find a method or a simple
attribute. If it's going to look in different places depending
on what it will find when it has looked...we have a Catch 22.

Normal methods in Python are defined in the scope of the class,
and they are passed the instance object as their first argument
when they are called. The call (where the instance object is
implicitly called in case of a bound method) is something that
comes after the lookup, as you can see in the a_f() example.

Python is *very* dynamic. The behaviour of the class can change
after the instance has been created.
class A: .... def f(self):
.... print 'Hello'
.... a = A()
a.f() Hello del A.f
a.f()

Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: A instance has no attribute 'f'

How would this work if 'a.f' doesn't cause a lookup in A if it's
missing in a? Do you want a.f to first search the instance a, then
the class A, and if it finds f in A, issue an AttributeError if it
turns out that f isn't a method?
Nov 4 '05 #96
Antoon Pardon wrote:
I have looked and didn't find it in the language reference.

This is what I have found:

An augmented assignment expression like x += 1 can be rewritten
as x = x + 1 to achieve a similar, but not exactly equal effect.
It's just a little further down. I'll post the quote once more (but
this is the last time ;^):

"""For targets which are attribute references, the initial value is
retrieved with a getattr() and the result is assigned with a setattr().
Notice that the two methods do not necessarily refer to the same
variable. When getattr() refers to a class variable, setattr() still
writes to an instance variable. For example:

class A:
x = 3 # class variable
a = A()
a.x += 1 # writes a.x as 4 leaving A.x as 3"""

I'd say it's documented...
That doesn't change the fact that the current behaviour is
on occasions awkward or whatever you want to call it.
I fear that this has to do with the way reality works. Perhaps
it's due to Gödel's incompleteness theorems... :)

Sure, Python has evolved and grown for about 15 years, and
backward compatibility has always been an issue, but the
management and development of Python is dynamic and fairly
open-minded. If there had been an obvious way to change this
in a way that solved more problems than it caused, I suspect
that change would have happened already.
I also find that people underestimate the magic that
is going on in python. But just because you are familiar with
the magic, doesn't make it less magic. IMO python shows its
history a little.
If Guido would design a new language today, that was aiming at
the kind of tasks Python solves, I'm sure it wouldn't be identical
to the current Python. The languages I'm most experienced in besides
Python are C++ and SQL. Compared to those beasts, Python is a wonder
in clarity and consistency. I don't think a single vendor has managed
to fully implement the SQL standard, and it's known that the standard
contains bugs, inconsitencies and gaps, even though (or because) it's
been worked on for more than 20 years. The C++ spec is marginally
better. Of course, there isn't a formal specification for Python. I
don't know if the language reference is so complete that someone
could actually write another really compatible Python implementation
based on just the reference manual. Still the difference is drastic.
I suspect that only few people in the SQL standard committee fully
understand the spec (maybe C.J. Date and Hugh Darwen does) and all
the things happening under the hood in C++ is staggering, considering
that this language is really just a fancy assmbler that can't even
manage memory for the programmer!
I'm after nothing particular. The only thing I'm frustrated about
is the way in which some people seem willing to defend python
just because it is python. If the only reaction I would have
gotten would have been something like: Yeah that seems a bit
awkward but fixing this would break more than it would cure,
I would have left it as it is.
That's probably what most people think, but we're not entirely
rational. We're human. An emotional posting will probably attract
equally emotional responses.
I have rarely indicated I wanted things to be fixed. Sure I would
like it if some things were different, but I recognize that there
are more important things that needs to be resolved.

Does that mean I shouldn't mention things that IMO could have been
better or that I should only mention them in the softest of
language that certainly can't be interpreted as emotional language.


Personally, I want comp.lang.python to work as a way for me to learn
new things about Python, to get help if I'm stuck with something, and
as a way for me to inform others about Python stuff. It's also a part
of the Python community--an arena where I communicate with other
Pythonistas. This is something important for me, both professionally
and personally. I try to think an extra time before I post messages.
It this message meaningful? Does it add anything of value? Am I just
repeating what is already said? Is this message likely to have some
kind of positive impact or will it just be ignored? Might I hurt
someone? Am I building useful relationships?

When it works as it should, people you've never met before will buy
you beer or lunch when you happen to be in their neighbourhood. At
least they might come up to you and chat if you go to a Python
conference. They might also offer you jobs or contracts etc. This
is really nice and valuable. Something to handle with care.
Nov 4 '05 #97
Antoon Pardon wrote:
Except when your default is a list

class foo:
x = [] # default

a = foo()
a.x += [3]

b = foo()
b.x

This results in [3]. So in this case using a class variable x to
provide a default empty list doesn't work out in combination
with augmented operators.


This has nothing to do with namespacing at all, it's the Python
idiosyncracy about operations on mutable types. In this case, +=
mutates an object, while + returns a new one -- as by definition, for
mutables.
Nov 4 '05 #98
Antoon Pardon wrote:
Well maybe because as far as I understand the same kind of logic
can be applied to something like

lst[f()] += foo

In order to decide that this should be equivallent to

lst[f()] = lst[f()] + foo.

But that isn't the case.


Because, surprisingly enough, Python tends to evaluate expressions only
once each time they're invoked.

In this case, [] is being used to get an item and set an item --
therefore, it /has/ to be invoked twice -- once for __getitem__, and
once for __setitem__.

Likewises, lst appears once, and it is used once -- the name gets looked
up once (which leads to a += 1 problems if a is in an outer scope).

f() also appears once -- so to evaluate it more trhan one time is odd,
at best.

If you know very much about modern lisps, it's similar to the difference
between a defun and a defmacro.
Nov 4 '05 #99
Antoon Pardon wrote:
Op 2005-11-04, Steve Holden schreef <st***@holdenweb.com>:
Antoon Pardon wrote:
Op 2005-11-04, Steve Holden schreef <st***@holdenweb.com>:
[...]
I suppose ultimately I'm just more pragmatic than you.
It has nothing to do with being more pragmatic. Being pragmatic
is about how you handle things with real life projects. It has
little to do with the degree in which you agree with the design
of the tool you have to work with. I would say I am more pragmatic
than most defenders of python, because when it comes done to
do my work, I just use python as best as I can, while a lot
of people here seem to think that every little criticism I have
is enough to go and look for a different language.


No, being pragmatic is to do with accepting what is rather than wasting
time wishing it were otherwise,

Just accepting what is, is not pragmatic. Not much progress would have
been made if we just accepted what is.

Pragmatically, I accept that whatever I say you are likely to respond
with a nit-picking argument. Pragmatically I accept this situation for
what it is. This does not stop me imagining a world where our dialogues
are about meaningful issues rather than whether a particular facet of
Python's design is "unsane". Pragmatically I accept that such a world is
likely to exist only in my imagination.
particularly when the "insane" behavior
was actually a deliberate design choice. Which is why it doesn't work
the same as non-local references in nested scopes.

That b.a = b.a + 2

works as a result of a design choice, that I can accept.

But IMO b.a += 2, working as it does, is more the result of
earlier design and implementation decisions than it was
a deliberate design decision.

You are wrong.

regards
Steve
--
Steve Holden +44 150 684 7255 +1 800 494 3119
Holden Web LLC www.holdenweb.com
PyCon TX 2006 www.python.org/pycon/

Nov 4 '05 #100

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

106
by: A | last post by:
Hi, I have always been taught to use an inialization list for initialising data members of a class. I realize that initialsizing primitives and pointers use an inialization list is exactly the...
5
by: xuatla | last post by:
Hi, I encountered the following compile error of c++ and hope to get your help. test2.cpp: In member function `CTest CTest::operator+=(CTest&)': test2.cpp:79: error: no match for 'operator='...
5
by: Chris | last post by:
Hi, I don't get the difference between a struct and a class ! ok, I know that a struct is a value type, the other a reference type, I understand the technical differences between both, but...
9
by: NevilleDNZ | last post by:
Can anyone explain why "begin B: 123" prints, but 456 doesn't? $ /usr/bin/python2.3 x1x2.py begin A: Pre B: 123 456 begin B: 123 Traceback (most recent call last): File "x1x2.py", line 13,...
14
by: lovecreatesbea... | last post by:
Could you tell me how many class members the C++ language synthesizes for a class type? Which members in a class aren't derived from parent classes? I have read the book The C++ Programming...
20
by: tshad | last post by:
Using VS 2003, I am trying to take a class that I created to create new variable types to handle nulls and track changes to standard variable types. This is for use with database variables. This...
20
by: d.s. | last post by:
I've got an app with two classes, and one class (InventoryInfoClass) is an object within the other class (InventoryItem). I'm running into problems with trying to access (get/set) a private...
16
by: John Doe | last post by:
Hi, I wrote a small class to enumerate available networks on a smartphone : class CNetwork { public: CNetwork() {}; CNetwork(CString& netName, GUID netguid): _netname(netName),...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.