Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #44944 > unrolled thread
| Started by | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| First post | 2013-05-08 08:52 +0000 |
| Last post | 2013-05-13 08:15 -0500 |
| Articles | 20 on this page of 82 — 23 participants |
Back to article view | Back to comp.lang.python
object.enable() anti-pattern Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-05-08 08:52 +0000
Re: object.enable() anti-pattern Christian Heimes <christian@python.org> - 2013-05-08 11:51 +0200
Re: object.enable() anti-pattern Robert Kern <robert.kern@gmail.com> - 2013-05-08 11:13 +0100
Re: object.enable() anti-pattern Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-05-08 12:30 +0000
Re: object.enable() anti-pattern Roy Smith <roy@panix.com> - 2013-05-08 09:17 -0400
Re: object.enable() anti-pattern Duncan Booth <duncan.booth@invalid.invalid> - 2013-05-08 14:27 +0000
Re: object.enable() anti-pattern Dan Sommers <dan@tombstonezero.net> - 2013-05-09 02:38 +0000
Re: object.enable() anti-pattern Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-05-09 05:37 +0000
Re: object.enable() anti-pattern Chris Angelico <rosuav@gmail.com> - 2013-05-09 15:52 +1000
Re: object.enable() anti-pattern Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-05-09 03:12 +0000
Re: object.enable() anti-pattern Dan Sommers <dan@tombstonezero.net> - 2013-05-09 02:42 +0000
Re: object.enable() anti-pattern Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-05-09 05:23 +0000
Re: object.enable() anti-pattern Terry Jan Reedy <tjreedy@udel.edu> - 2013-05-09 02:41 -0400
Re: object.enable() anti-pattern Gregory Ewing <greg.ewing@canterbury.ac.nz> - 2013-05-09 19:54 +1200
Re: object.enable() anti-pattern Cameron Simpson <cs@zip.com.au> - 2013-05-09 18:23 +1000
Re: object.enable() anti-pattern Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-05-09 11:30 +0000
Re: object.enable() anti-pattern Cameron Simpson <cs@zip.com.au> - 2013-05-10 09:36 +1000
Re: object.enable() anti-pattern Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-05-10 05:00 +0000
Re: object.enable() anti-pattern Roy Smith <roy@panix.com> - 2013-05-10 01:50 -0400
Re: object.enable() anti-pattern Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-05-10 09:47 +0000
Re: object.enable() anti-pattern Roy Smith <roy@panix.com> - 2013-05-10 09:22 -0400
Re: object.enable() anti-pattern Cameron Simpson <cs@zip.com.au> - 2013-05-11 08:25 +1000
Re: object.enable() anti-pattern Mark Janssen <dreamingforward@gmail.com> - 2013-05-10 20:16 -0700
Re: object.enable() anti-pattern Thomas Rachel <nutznetz-0c1b6768-bfa9-48d5-a470-7603bd3aa915@spamschutz.glglgl.de> - 2013-05-11 06:21 +0200
Re: object.enable() anti-pattern Mark Janssen <dreamingforward@gmail.com> - 2013-05-10 21:00 -0700
Re: object.enable() anti-pattern Dennis Lee Bieber <wlfraed@ix.netcom.com> - 2013-05-11 15:04 -0400
Re: object.enable() anti-pattern Greg Ewing <greg.ewing@canterbury.ac.nz> - 2013-05-10 10:56 +1200
Re: object.enable() anti-pattern Roy Smith <roy@panix.com> - 2013-05-09 09:07 -0400
Re: object.enable() anti-pattern Oscar Benjamin <oscar.j.benjamin@gmail.com> - 2013-05-09 14:51 +0100
Re: object.enable() anti-pattern Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-05-09 18:21 +0000
Re: object.enable() anti-pattern MRAB <python@mrabarnett.plus.com> - 2013-05-09 19:34 +0100
Re: object.enable() anti-pattern Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-05-10 02:30 +0000
Re: object.enable() anti-pattern Roy Smith <roy@panix.com> - 2013-05-09 23:09 -0400
Re: object.enable() anti-pattern Mark Janssen <dreamingforward@gmail.com> - 2013-05-09 20:19 -0700
Re: object.enable() anti-pattern Chris Angelico <rosuav@gmail.com> - 2013-05-10 13:46 +1000
Re: object.enable() anti-pattern Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-05-10 05:03 +0000
Re: object.enable() anti-pattern Dan Sommers <dan@tombstonezero.net> - 2013-05-10 06:22 +0000
Re: object.enable() anti-pattern Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-05-10 11:00 +0000
Re: object.enable() anti-pattern Robert Kern <robert.kern@gmail.com> - 2013-05-10 13:19 +0100
Re: object.enable() anti-pattern Roy Smith <roy@panix.com> - 2013-05-10 10:01 -0400
Re: object.enable() anti-pattern Oscar Benjamin <oscar.j.benjamin@gmail.com> - 2013-05-10 15:29 +0100
Re: object.enable() anti-pattern Roy Smith <roy@panix.com> - 2013-05-10 10:37 -0400
Re: object.enable() anti-pattern Chris Angelico <rosuav@gmail.com> - 2013-05-11 00:46 +1000
Re: object.enable() anti-pattern Roy Smith <roy@panix.com> - 2013-05-10 10:54 -0400
Re: object.enable() anti-pattern Chris Angelico <rosuav@gmail.com> - 2013-05-11 01:09 +1000
Re: object.enable() anti-pattern Roy Smith <roy@panix.com> - 2013-05-10 11:21 -0400
Re: object.enable() anti-pattern Chris Angelico <rosuav@gmail.com> - 2013-05-11 01:44 +1000
Re: object.enable() anti-pattern Robert Kern <robert.kern@gmail.com> - 2013-05-10 16:33 +0100
Re: object.enable() anti-pattern Serhiy Storchaka <storchaka@gmail.com> - 2013-05-10 18:44 +0300
Re: object.enable() anti-pattern André Malo <ndparker@gmail.com> - 2013-05-11 17:33 +0200
Re: object.enable() anti-pattern Chris Angelico <rosuav@gmail.com> - 2013-05-12 02:04 +1000
Re: object.enable() anti-pattern Robert Kern <robert.kern@gmail.com> - 2013-05-10 18:20 +0100
Re: object.enable() anti-pattern Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-05-11 07:51 +0000
Re: object.enable() anti-pattern Roy Smith <roy@panix.com> - 2013-05-11 09:31 -0400
Re: object.enable() anti-pattern Robert Kern <robert.kern@gmail.com> - 2013-05-11 20:55 +0100
Re: object.enable() anti-pattern Chris Angelico <rosuav@gmail.com> - 2013-05-12 08:39 +1000
Re: object.enable() anti-pattern Chris Angelico <rosuav@gmail.com> - 2013-05-11 03:24 +1000
Re: object.enable() anti-pattern Mark Janssen <dreamingforward@gmail.com> - 2013-05-10 19:43 -0700
Re: object.enable() anti-pattern Wayne Werner <wayne@waynewerner.com> - 2013-05-12 11:48 -0500
Re: object.enable() anti-pattern Terry Jan Reedy <tjreedy@udel.edu> - 2013-05-12 16:23 -0400
Re: object.enable() anti-pattern Mark Janssen <dreamingforward@gmail.com> - 2013-05-09 20:51 -0700
Re: object.enable() anti-pattern Chris Angelico <rosuav@gmail.com> - 2013-05-10 13:08 +1000
Re: object.enable() anti-pattern roy@panix.com (Roy Smith) - 2013-05-09 14:59 -0400
Re: object.enable() anti-pattern Chris Angelico <rosuav@gmail.com> - 2013-05-10 07:55 +1000
Re: object.enable() anti-pattern Nobody <nobody@nowhere.com> - 2013-05-10 17:59 +0100
Re: object.enable() anti-pattern Roy Smith <roy@panix.com> - 2013-05-10 13:32 -0400
Re: object.enable() anti-pattern Dennis Lee Bieber <wlfraed@ix.netcom.com> - 2013-05-11 15:24 -0400
Re: object.enable() anti-pattern Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-05-11 07:05 +0000
Re: object.enable() anti-pattern Mark Janssen <dreamingforward@gmail.com> - 2013-05-08 19:53 -0700
Re: object.enable() anti-pattern Mark Janssen <dreamingforward@gmail.com> - 2013-05-08 19:56 -0700
Re: object.enable() anti-pattern Wayne Werner <wayne@waynewerner.com> - 2013-05-09 06:08 -0500
Re: object.enable() anti-pattern Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-05-09 11:51 +0000
Re: object.enable() anti-pattern Gregory Ewing <greg.ewing@canterbury.ac.nz> - 2013-05-10 11:43 +1200
Re: object.enable() anti-pattern Michael Speer <knomenet@gmail.com> - 2013-05-09 20:18 -0400
Re: object.enable() anti-pattern Roy Smith <roy@panix.com> - 2013-05-09 20:50 -0400
Re: object.enable() anti-pattern Wayne Werner <wayne@waynewerner.com> - 2013-05-12 12:14 -0500
Re: object.enable() anti-pattern Terry Jan Reedy <tjreedy@udel.edu> - 2013-05-12 16:03 -0400
Re: object.enable() anti-pattern Greg Ewing <greg.ewing@canterbury.ac.nz> - 2013-05-13 11:18 +1200
Re: object.enable() anti-pattern Fábio Santos <fabiosantosart@gmail.com> - 2013-05-13 07:32 +0100
Re: object.enable() anti-pattern Chris Angelico <rosuav@gmail.com> - 2013-05-13 17:36 +1000
Re: object.enable() anti-pattern Fábio Santos <fabiosantosart@gmail.com> - 2013-05-13 09:09 +0100
Re: object.enable() anti-pattern Wayne Werner <wayne@waynewerner.com> - 2013-05-13 08:15 -0500
Page 3 of 5 — ← Prev page 1 2 [3] 4 5 Next page →
| From | Oscar Benjamin <oscar.j.benjamin@gmail.com> |
|---|---|
| Date | 2013-05-10 15:29 +0100 |
| Message-ID | <mailman.1530.1368196163.3114.python-list@python.org> |
| In reply to | #45098 |
On 10 May 2013 15:01, Roy Smith <roy@panix.com> wrote: > In article <mailman.1527.1368188358.3114.python-list@python.org>, > Robert Kern <robert.kern@gmail.com> wrote: > >> I'd be curious to see in-the-wild instances of the anti-pattern that >> you are talking about, then. I think everyone agrees that entirely >> unmotivated "enable" methods should be avoided, but I have my doubts >> that they come up very often. > > As I mentioned earlier in this thread, this was a common pattern in the > early days of C++, when exceptions were a new concept and handled poorly > by many compilers (and, for that matter, programmers). > > There was a school of thought that constructors should never be able to > fail (because the only way for a constructor to fail is to throw an > exception). The pattern was to always have the constructor succeed, and > then either have a way to check to see if the newly-constructed object > was valid, or have a separate post-construction initialization step > which could fail. It's not just because of exceptions. In C++ virtual method calls in a constructor for a class A will always call the methods of class A even if the object being constructed is actually of a subclass B because the B part of the object isn't initialised when the A constructor is called. There may be a better way to do this since I last used C++ but as I remember it the two-phase pattern was a recommended way to implement polymorphic behaviour during initialisation. Oscar
[toc] | [prev] | [next] | [standalone]
| From | Roy Smith <roy@panix.com> |
|---|---|
| Date | 2013-05-10 10:37 -0400 |
| Message-ID | <roy-0C2D3D.10373710052013@news.panix.com> |
| In reply to | #45099 |
In article <mailman.1530.1368196163.3114.python-list@python.org>, Oscar Benjamin <oscar.j.benjamin@gmail.com> wrote: > It's not just because of exceptions. In C++ virtual method calls in a > constructor for a class A will always call the methods of class A even > if the object being constructed is actually of a subclass B because > the B part of the object isn't initialised when the A constructor is > called. There may be a better way to do this since I last used C++ but > as I remember it the two-phase pattern was a recommended way to > implement polymorphic behaviour during initialisation. Mind. Blown. One of the things I love (FSVO love) about C++ is that no matter how much I learn, there's always whole new areas of wonderment to explore behind doors I didn't even know existed. Thank you. I suppose, if I had a class like this, I would write a factory function which called the constructor and post-construction initializer. And then I would make the constructor protected.
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2013-05-11 00:46 +1000 |
| Message-ID | <mailman.1531.1368197225.3114.python-list@python.org> |
| In reply to | #45100 |
On Sat, May 11, 2013 at 12:37 AM, Roy Smith <roy@panix.com> wrote: > I suppose, if I had a class like this, I would write a factory function > which called the constructor and post-construction initializer. And > then I would make the constructor protected. That sounds like a reasonable plan, with the possible exception of protected. Since meeting Python, I've stopped using private and protected anywhere. ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Roy Smith <roy@panix.com> |
|---|---|
| Date | 2013-05-10 10:54 -0400 |
| Message-ID | <roy-E4B847.10545410052013@news.panix.com> |
| In reply to | #45101 |
In article <mailman.1531.1368197225.3114.python-list@python.org>, Chris Angelico <rosuav@gmail.com> wrote: > On Sat, May 11, 2013 at 12:37 AM, Roy Smith <roy@panix.com> wrote: > > I suppose, if I had a class like this, I would write a factory function > > which called the constructor and post-construction initializer. And > > then I would make the constructor protected. > > That sounds like a reasonable plan, with the possible exception of > protected. Since meeting Python, I've stopped using private and > protected anywhere. > > ChrisA Each language has its own set of best practices. Trying to write C++ code using Python patterns is as bad as trying to write Python code using C++ patterns.
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2013-05-11 01:09 +1000 |
| Message-ID | <mailman.1532.1368198547.3114.python-list@python.org> |
| In reply to | #45102 |
On Sat, May 11, 2013 at 12:54 AM, Roy Smith <roy@panix.com> wrote:
> In article <mailman.1531.1368197225.3114.python-list@python.org>,
> Chris Angelico <rosuav@gmail.com> wrote:
>
>> On Sat, May 11, 2013 at 12:37 AM, Roy Smith <roy@panix.com> wrote:
>> > I suppose, if I had a class like this, I would write a factory function
>> > which called the constructor and post-construction initializer. And
>> > then I would make the constructor protected.
>>
>> That sounds like a reasonable plan, with the possible exception of
>> protected. Since meeting Python, I've stopped using private and
>> protected anywhere.
>>
>> ChrisA
>
> Each language has its own set of best practices. Trying to write C++
> code using Python patterns is as bad as trying to write Python code
> using C++ patterns.
Agreed, in generality. But what is actually gained by hiding data from
yourself? Compare:
class Foo
{
int asdf;
public:
Foo(int _asdf):asdf(_asdf) {}
int get_asdf() {return asdf;}
void set_asdf(int _asdf) {asdf=_asdf;}
void frob() {printf("Hi, I am %d\n",asdf);}
};
struct Foo
{
int asdf;
Foo(int _asdf):asdf(_asdf) {}
void frob() {printf("Hi, I am %d\n",asdf);}
};
Is there anything worse about the second one? Oh, and by logical
extension, here's something that doesn't (AFAIK) work in C++, but does
in another language that's syntactically similar:
class Foo(int asdf)
{
void frob() {write("Hi, I am %d\n",asdf);}
}
Now that's brevity. Why bother saying what's patently obvious? (Pike's
"class" keyword is like C++'s "struct", members are public by
default.)
ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Roy Smith <roy@panix.com> |
|---|---|
| Date | 2013-05-10 11:21 -0400 |
| Message-ID | <roy-3A4FCB.11213310052013@news.panix.com> |
| In reply to | #45104 |
In article <mailman.1532.1368198547.3114.python-list@python.org>, Chris Angelico <rosuav@gmail.com> wrote: > > Each language has its own set of best practices. Trying to write C++ > > code using Python patterns is as bad as trying to write Python code > > using C++ patterns. > > Agreed, in generality. But what is actually gained by hiding data from > yourself? You're not hiding it from yourself. You're hiding it from the other people who are using your code and may not understand all the subtle gotchas as well as you do. In the calling-virtual-methods-in-the-constructor case we're talking about here, constructing an object by calling B() without immediately following it up with a call to B::PostInit() is dangerous. If you document that you shouldn't do that, but allow it anyway, people will do it. Better to disallow it completely. People will bitch when their code doesn't compile, but that's better than compiling and running and producing the wrong results. You solve this problem in Python by simply having the constructor do the right thing.
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2013-05-11 01:44 +1000 |
| Message-ID | <mailman.1538.1368200674.3114.python-list@python.org> |
| In reply to | #45106 |
On Sat, May 11, 2013 at 1:21 AM, Roy Smith <roy@panix.com> wrote: > In article <mailman.1532.1368198547.3114.python-list@python.org>, > Chris Angelico <rosuav@gmail.com> wrote: >> >> Agreed, in generality. But what is actually gained by hiding data from >> yourself? > > You're not hiding it from yourself. You're hiding it from the other > people who are using your code and may not understand all the subtle > gotchas as well as you do. True. And on looking over my code, I find that there are a few cases where I've used private members: I have a buffer class that acts pretty much like a non-refcounted string, and it declares a private copy constructor to prevent accidental misuse. But it's the exception, not the rule. My main point isn't about the cases where you actually want to prevent access, but the all-too-common case where the member itself is private and there are two public methods to get and set it. Massive boilerplate. Completely unnecessary in 99%+ of cases. ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Robert Kern <robert.kern@gmail.com> |
|---|---|
| Date | 2013-05-10 16:33 +0100 |
| Message-ID | <mailman.1537.1368200002.3114.python-list@python.org> |
| In reply to | #45098 |
On 2013-05-10 15:01, Roy Smith wrote: > In article <mailman.1527.1368188358.3114.python-list@python.org>, > Robert Kern <robert.kern@gmail.com> wrote: > >> I'd be curious to see in-the-wild instances of the anti-pattern that >> you are talking about, then. I think everyone agrees that entirely >> unmotivated "enable" methods should be avoided, but I have my doubts >> that they come up very often. > > As I mentioned earlier in this thread, this was a common pattern in the > early days of C++, when exceptions were a new concept and handled poorly > by many compilers (and, for that matter, programmers). > > There was a school of thought that constructors should never be able to > fail (because the only way for a constructor to fail is to throw an > exception). The pattern was to always have the constructor succeed, and > then either have a way to check to see if the newly-constructed object > was valid, or have a separate post-construction initialization step > which could fail. > > See, for example, the isValid() and Exists() calls for RogueWave's > RWFile class (http://tinyurl.com/c8kv26g). And also, > http://tinyurl.com/cgs6clx. > > Even today, there are C++ implementations which do not use exceptions. > Some are for use in embedded or real-time systems where things need to > be strictly time-bound and/or memory-bound. Others are for historical > reasons (http://tinyurl.com/6hn4zo). > > Once people were used to writing "can't fail" constructors in C++, they > often continued using that pattern in other languages, where the > underlying reasons no longer made sense. Quite possibly, they never > even knew the underlying reasons; they were taught, "Constructors must > never fail", and assumed it was a universal rule. Right, this is one of the "bad reasons" I talk about later in my message. The authors had a reason in their mind for doing it (they thought it was a universal rule); it was just a bad one. It's more useful to talk about why people thought they should follow that pattern than to just say "there is no reason to do this". > This, BTW, is one of my biggest beefs with the classic Gang Of Four > pattern book. It presents a bunch of patterns as being universally > applicable, when in reality many (if not most) of them are highly C++ > specific. > > BTW, whenever I read things like, "I think everyone agrees", I > automatically assume what the writer really meant was, "I, and all the > people who agree with me, think". Hah! Fair enough. I actually meant it to emphasize that I thought that Steven was overly reducing his statements to something that was trivially true, sacrificing content for validity. I will suggest that your interpretation of that phrase is more appropriate when the speaker is proposing something of their own rather than (partially) conceding a point. The exaggeration is only self-aggrandizing in the former case. -- Robert Kern "I have come to believe that the whole world is an enigma, a harmless enigma that is made terrible by our own mad attempt to interpret it as though it had an underlying truth." -- Umberto Eco
[toc] | [prev] | [next] | [standalone]
| From | Serhiy Storchaka <storchaka@gmail.com> |
|---|---|
| Date | 2013-05-10 18:44 +0300 |
| Message-ID | <mailman.1541.1368205876.3114.python-list@python.org> |
| In reply to | #45092 |
10.05.13 15:19, Robert Kern написав(ла):
> I'd be curious to see in-the-wild instances of the anti-pattern that you
> are talking about, then.
Many (if not most) GUI frameworks use this pattern.
button = Button("text")
button.setForegroundColor(...)
button.setBackgoundColor(...)
button.setFont(...)
button.setRelief(...)
button.setBorder(...)
button.setWidth(...)
button.setAction(...)
button.setMouseListener(...)
button.place(...)
Another example is running a subprocess in Unix-like systems.
fork()
open/close file descriptors, set limits, etc
exec*()
[toc] | [prev] | [next] | [standalone]
| From | André Malo <ndparker@gmail.com> |
|---|---|
| Date | 2013-05-11 17:33 +0200 |
| Message-ID | <2048586.oD9bP8C7rC@news.perlig.de> |
| In reply to | #45116 |
* Serhiy Storchaka wrote:
> Another example is running a subprocess in Unix-like systems.
>
> fork()
> open/close file descriptors, set limits, etc
> exec*()
For running a subprocess, only fork() is needed. For starting another
executable, only exec() is needed. For running the new executable in a
subprocess fork() and exec() are needed. I think, that's a bad example.
These APIs are actually well-designed.
nd
--
Gefunden auf einer "Webdesigner"-Seite:
> Programmierung in HTML, XML, WML, CGI, FLASH <
# André Malo # http://pub.perlig.de/ #
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2013-05-12 02:04 +1000 |
| Message-ID | <mailman.1559.1368288293.3114.python-list@python.org> |
| In reply to | #45141 |
On Sun, May 12, 2013 at 1:33 AM, André Malo <ndparker@gmail.com> wrote: > * Serhiy Storchaka wrote: > >> Another example is running a subprocess in Unix-like systems. >> >> fork() >> open/close file descriptors, set limits, etc >> exec*() > > For running a subprocess, only fork() is needed. For starting another > executable, only exec() is needed. For running the new executable in a > subprocess fork() and exec() are needed. I think, that's a bad example. > These APIs are actually well-designed. That said, though, there's certainly plenty of room for one-call APIs like popen. For the simple case where you want to start a process with some other executable, wait for it to finish, and then work with its return value, it makes sense to hide the details of fork/exec/wait - especially since that simple API can be cross-platform, where fork() itself is quite hard to implement on Windows. ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Robert Kern <robert.kern@gmail.com> |
|---|---|
| Date | 2013-05-10 18:20 +0100 |
| Message-ID | <mailman.1542.1368206453.3114.python-list@python.org> |
| In reply to | #45092 |
On 2013-05-10 16:44, Serhiy Storchaka wrote:
> 10.05.13 15:19, Robert Kern написав(ла):
>> I'd be curious to see in-the-wild instances of the anti-pattern that you
>> are talking about, then.
>
> Many (if not most) GUI frameworks use this pattern.
>
> button = Button("text")
> button.setForegroundColor(...)
> button.setBackgoundColor(...)
> button.setFont(...)
> button.setRelief(...)
> button.setBorder(...)
> button.setWidth(...)
> button.setAction(...)
> button.setMouseListener(...)
> button.place(...)
>
> Another example is running a subprocess in Unix-like systems.
>
> fork()
> open/close file descriptors, set limits, etc
> exec*()
According to Steven's criteria, neither of these are instances of the
anti-pattern because there are good reasons they are this way. He is reducing
the anti-pattern to just those cases where there is no reason for doing so. That
is why I asked for in-the-wild instances. I should have qualified my sentence to
include "according to your criteria" because people seem to be answering my
query out of that context.
--
Robert Kern
"I have come to believe that the whole world is an enigma, a harmless enigma
that is made terrible by our own mad attempt to interpret it as though it had
an underlying truth."
-- Umberto Eco
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2013-05-11 07:51 +0000 |
| Message-ID | <518df898$0$29997$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #45117 |
On Fri, 10 May 2013 18:20:34 +0100, Robert Kern wrote: > According to Steven's criteria, neither of these are instances of the > anti-pattern because there are good reasons they are this way. He is > reducing the anti-pattern to just those cases where there is no reason > for doing so. But isn't that the case for all anti-patterns? We agree that GOTO is an anti-pattern. That doesn't mean that there aren't valid reasons for using GOTO. Sometimes there are good use-cases for GOTO that outweigh the harm. By calling it an anti-pattern, though, we shift the onus onto the person wanting to use GOTO: justify why you need it, or use something else. Would you object less if I called it a "code smell" than an "anti- pattern"? If so, I accept your criticism, and call it a code smell: the less temporal coupling your API has, the better. > That is why I asked for in-the-wild instances. How about this? http://legacy.thecodewhisperer.com/post/366626867/improving-clarity-by-removing-temporal-coupling Another example of temporal coupling is json_decode in PHP: you must follow it by a call to json_last_error, otherwise you have no way of telling whether the json_decode function succeeded or not. http://php.net/manual/en/function.json-last-error.php > I should > have qualified my sentence to include "according to your criteria" > because people seem to be answering my query out of that context. Now you know how I feel :-) I started this thread asking for help tracking down a blog post describing this code smell, or at least the name of such. Thanks again to Wayne Werner, who came through with the name of the anti-pattern, temporal coupling, and a blog post describing it, although not the one I read all those many moons ago. I never intended to give the impression that *any* use of a separate "enable" method call was bad. I certainly didn't intend to be bogged down into a long discussion about the minutia of file descriptors in C, but it was educational :-) -- Steven
[toc] | [prev] | [next] | [standalone]
| From | Roy Smith <roy@panix.com> |
|---|---|
| Date | 2013-05-11 09:31 -0400 |
| Message-ID | <roy-6A558E.09311211052013@news.panix.com> |
| In reply to | #45128 |
In article <518df898$0$29997$c3e8da3$5496439d@news.astraweb.com>, Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote: > I never intended to give the impression that *any* use of a separate > "enable" method call was bad. I certainly didn't intend to be bogged > down into a long discussion about the minutia of file descriptors in > C, but it was educational :-) Well, you did say you were here for abuse. I think you got your money's worth. Can I interest you in a course of getting hit on the head lessons? And just to be clear to the studio audience and all of you who are watching at home... For all the effort I put into nit-picking, I do agree with Steven's basic premise. Two-phase construction is usually not the right way to be designing classes. Especially in languages like Python where constructors raising exceptions is both inexpensive and universally accepted as normal behavior.
[toc] | [prev] | [next] | [standalone]
| From | Robert Kern <robert.kern@gmail.com> |
|---|---|
| Date | 2013-05-11 20:55 +0100 |
| Message-ID | <mailman.1565.1368302161.3114.python-list@python.org> |
| In reply to | #45128 |
On 2013-05-11 08:51, Steven D'Aprano wrote: > On Fri, 10 May 2013 18:20:34 +0100, Robert Kern wrote: > >> According to Steven's criteria, neither of these are instances of the >> anti-pattern because there are good reasons they are this way. He is >> reducing the anti-pattern to just those cases where there is no reason >> for doing so. > > But isn't that the case for all anti-patterns? > > We agree that GOTO is an anti-pattern. That doesn't mean that there > aren't valid reasons for using GOTO. Sometimes there are good use-cases > for GOTO that outweigh the harm. By calling it an anti-pattern, though, > we shift the onus onto the person wanting to use GOTO: justify why you > need it, or use something else. Yes, that was the point I was making. You seemed to be defining away the legitimate instances as not instances of the pattern at all because they were legitimate, and that appeared to me to be defeating the purpose of having the discussion. On a related note, I *don't* think it's a good idea to phrase it as "justify why you need it". I don't think that gives very good guidance to a novice when they are given the task of designing something. People can come up with a justification for just about anything, especially when they are only justifying things to themselves. I think it's more important to just talk about the situations where a pattern is useful, and the common situations where people, for whatever reason, *think* that a pattern is useful, but isn't. Naming it a Pattern or Anti-pattern is really just a measure of how bad the latter half of that is compared to the first half, and is less interesting than the discussion itself. That's why I had a bug up my ass about what looked like the exclusion of the "good" uses. It's the good examples that give novices an idea of what a good justification looks like, so they can tell if the justification they are giving themselves measures up. > Would you object less if I called it a "code smell" than an "anti- > pattern"? If so, I accept your criticism, and call it a code smell: the > less temporal coupling your API has, the better. That was not really my objection. I was objecting to the way you appeared to be defining the particular pattern in question. But as we appear to agree on the important matters, I won't press it further. >> That is why I asked for in-the-wild instances. > > How about this? > > http://legacy.thecodewhisperer.com/post/366626867/improving-clarity-by-removing-temporal-coupling There's something about Java mixedCase that makes my eyes glaze, so I'll take your word for it. :-) > Another example of temporal coupling is json_decode in PHP: you must > follow it by a call to json_last_error, otherwise you have no way of > telling whether the json_decode function succeeded or not. > > http://php.net/manual/en/function.json-last-error.php I suspect that the author might say something about error checking being optional. But yeah, terrible API. :-) -- Robert Kern "I have come to believe that the whole world is an enigma, a harmless enigma that is made terrible by our own mad attempt to interpret it as though it had an underlying truth." -- Umberto Eco
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2013-05-12 08:39 +1000 |
| Message-ID | <mailman.1569.1368311970.3114.python-list@python.org> |
| In reply to | #45128 |
On Sun, May 12, 2013 at 5:55 AM, Robert Kern <robert.kern@gmail.com> wrote: >> Another example of temporal coupling is json_decode in PHP: you must >> follow it by a call to json_last_error, otherwise you have no way of >> telling whether the json_decode function succeeded or not. >> >> http://php.net/manual/en/function.json-last-error.php > > > I suspect that the author might say something about error checking being > optional. But yeah, terrible API. :-) The problem with that one isn't that error checking is optional, but that errors are signalled with a perfectly legal return value (FALSE, if I recall correctly - which is also returned if you json_decode a boolean false). Better design would either throw an exception on error, or have a unique sentinel representing the errorness of the return value. ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2013-05-11 03:24 +1000 |
| Message-ID | <mailman.1543.1368206709.3114.python-list@python.org> |
| In reply to | #45092 |
On Sat, May 11, 2013 at 1:44 AM, Serhiy Storchaka <storchaka@gmail.com> wrote:
> 10.05.13 15:19, Robert Kern написав(ла):
>
>> I'd be curious to see in-the-wild instances of the anti-pattern that you
>> are talking about, then.
>
>
> Many (if not most) GUI frameworks use this pattern.
>
> button = Button("text")
> button.setForegroundColor(...)
> button.setBackgoundColor(...)
> button.setFont(...)
> button.setRelief(...)
> button.setBorder(...)
> button.setWidth(...)
> button.setAction(...)
> button.setMouseListener(...)
> button.place(...)
The button really exists, though. You could merge the creation and
placement (or in the case of a window/dialog, the creation and
showing), but it's often useful to not. However, in the specific case
you have there, there's an alternative: a mapping of attributes and
values passed to the constructor, and then you could pass the
constructed object directly to a place(). That would let you, if you
wished, effectively construct a Button with a parent right there,
which makes reasonable sense.
> Another example is running a subprocess in Unix-like systems.
>
> fork()
> open/close file descriptors, set limits, etc
> exec*()
Hrm. Not really a corresponding example. After you fork, you have two
actual processes. Following up with an exec is only one of the
possible options; I've done code that forks and execs, and code that
forks and keeps running, and neither of them feels "wrong" in any way.
There IS a function that's similar to what you're saying, and that's
vfork:
"""
(From POSIX.1) The vfork() function has the same effect as fork(2),
except that the behavior is undefined if the process created by
vfork() either modifies any data other than a variable of type pid_t
used to store the return value from vfork(), or returns from the
function in which vfork() was called, or calls any other function
before successfully calling _exit(2) or one of the exec(3) family of
functions.
"""
It's deprecated because it's so fragile (and because regular fork()
isn't that much less efficient now; AIUI, vfork was meant to be a
lightweight fork). I would say that the deprecation of vfork in favour
of fork is a strong indication that the object.enable() anti-pattern
can come up in kernel APIs too, and isn't liked there either.
ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Mark Janssen <dreamingforward@gmail.com> |
|---|---|
| Date | 2013-05-10 19:43 -0700 |
| Message-ID | <mailman.1545.1368240240.3114.python-list@python.org> |
| In reply to | #45092 |
>>>>>> There is no sensible use-case for creating a file OBJECT unless it
>>>>>> initially wraps an open file pointer.
>>
>>> So far the only counter-examples given aren't counter-examples ...
>>
>> Well, sure, if you discount operations like "create this file" and
>> queries like "could I delete this file if I wanted to?" [0] as methods
>> of the file system rather than of a hypothetical file object.
>>
>> What about a distributed system? Suppose I want to create a file object
>> in one place, and send that object to the another place for the file to
>> be read from or written to [1]? Suppose that for security reasons, I
>> have to do it that way, because the first place can only create the
>> objects, and the second place can only access the underly file contents
>> through an existing object?
>
> Unless you have re-implemented the file I/O system, it doesn't matter. If
> your file objects are based on C I/O, then even if the first server
> cannot read or write to the files it still creates file objects in an
> open state, because that is how C works.
>
> Or maybe the first server only creates some sort of proxy to the real
> underlying file object. Or maybe you're re-implemented the I/O system,
> and aren't following C's design. Since you're just making this up as a
> thought experiment, anything is possible.
>
> But either way, that's fine. You've found an object where it does make
> sense to have an explicit "make it go" method: first one entity has
> permission to construct the object, but not to open the underlying file.
> Another entity has permission to open the underlying file, but not to
> create the object. I have no idea whether this is a reasonable security
> design or not, it actually sounds a bit rubbish to me but what do I know?
> So let's treat it as a reasonable design.
>
> As I've said, repeatedly, that's not what I'm talking about.
>
> When you DON'T have useful things that can be done with the object before
> calling "enable", then it is an anti-pattern to require a separate call
> to "enable" method, and the enable functionality should be moved into the
> object constructor. If you DO have useful things that can be done, like
> pass the object to another entity, for security, then that's a whole
> 'nuther story.
You're missing one other case: if there's useful things that can be
checked before calling enable(). Remember, on multi-user and/or
multi-processing systems, there could be contention for a slow
resource. If you automatically open a file for write, you're
preventing everyone else from writing and potentially reading it. So
there is something useful: did that file exist? Is that resource
available for writing?
Prior to such hi-level languages like Python and reliable hardware,
such fine-grained control was important and vital. Now it can
probably be relegated to special OS libraries.
Mark
> Really, what I'm describing is *normal* behaviour for most objects. We
> don't usually design APIs like this:
>
> n = int("42")
> n.enable()
> m = n + 1
> m.enable()
> x = m*2 + n*3
> print x - 1 # oops, raises because I forgot to call x.enable()
Again, you only do that for shared resources. In this case, memory
would have to be a (protected) shared resources, but the OS manages
memory allocation, so it's not an issue.
Mark
> That's a rubbish API, and for simple data-like objects, we all agree it
> is a rubbish API. So why accept the same rubbish API just because the
> object is more complicated?
I think I just told you, but let me know ..... :)
> For my next controversial opinion, I'm going to argue that we should do
> arithmetic using numbers rather than by inserting lists inside other
> lists:
Try arguing that we should have a common message-passing syntax.
> # Do this:
>
> count = 0
> count += 1
>
> # Not this:
>
> count = []
> count.insert(0, [])
That's actually what they do in "set theory", believe it or not.
MarkJ
Tacoma, Washington
[toc] | [prev] | [next] | [standalone]
| From | Wayne Werner <wayne@waynewerner.com> |
|---|---|
| Date | 2013-05-12 11:48 -0500 |
| Message-ID | <mailman.1585.1368377382.3114.python-list@python.org> |
| In reply to | #45092 |
On Fri, 10 May 2013, Robert Kern wrote:
> On 2013-05-10 12:00, Steven D'Aprano wrote:
>
>> But either way, that's fine. You've found an object where it does make
>> sense to have an explicit "make it go" method: first one entity has
>> permission to construct the object, but not to open the underlying file.
>> Another entity has permission to open the underlying file, but not to
>> create the object. I have no idea whether this is a reasonable security
>> design or not, it actually sounds a bit rubbish to me but what do I know?
>> So let's treat it as a reasonable design.
>>
>> As I've said, repeatedly, that's not what I'm talking about.
>>
>> When you DON'T have useful things that can be done with the object before
>> calling "enable", then it is an anti-pattern to require a separate call
>> to "enable" method, and the enable functionality should be moved into the
>> object constructor. If you DO have useful things that can be done, like
>> pass the object to another entity, for security, then that's a whole
>> 'nuther story.
>
> I'd be curious to see in-the-wild instances of the anti-pattern that you are
> talking about, then. I think everyone agrees that entirely unmotivated
> "enable" methods should be avoided, but I have my doubts that they come up
> very often. Do programmers have a natural tendency to make an extra,
> completely unnecessary method? I would think that they have a natural
> tendency to the opposite.
>
> In my experience, everyone has a reason in mind when they follow a
> pattern/anti-pattern. It is pretty rare that someone just does some specific,
> nameable thing for no reason at all. There is no need to call out an
> anti-pattern for which no one has a reason to do it. But there is a continuum
> of reasons. Some reasons are better than others. Some reasons only apply in a
> small set of circumstances but seem like they would apply more generally, at
> least to novice programmers. Programmers can be wrong about what they think
> the (anti-)pattern actually achieves. The whole point of naming an
> anti-pattern is to discuss those reasons, show where they are misapplied,
> where YAGNI, why novices overuse it, other patterns that should be used
> instead, and also the circumstances where it is actually a good pattern
> instead.
I'll share the anti-pattern that I've seen many times (not actually in
Python)
class CoolPresenter:
def __init__(self):
self.view = None
self.some_property = None
self.other_property = None
def initialize(self):
self.view.disable()
data = self.load_data()
self.view.data = data
self.view.enable()
def reload(self):
if self.view is None:
raise NotInitializedError("Error: Please setup class")
self.view.disable()
data = self.load_data()
self.view.data = data
self.view.enable()
Then you would see code like this:
presenter = CoolPresenter()
presenter.view = CoolView()
This is just plain silly for a few reasons:
- It's ambiguous. I don't know what's required for the CoolPresenter
to function properly.
- The temporal coupling mentioned earlier. I can create an instance of
a class and then call a function (say `reload`) and then boom! My
program crashes. There is *no possible* use case of this class where
you can use it without a view.
The motivation behind this anti-pattern that I've seen is the desire to
not have a constructor that "does too much". So you end out with an empty
constructor and temporal coupling, and a terrible API that doesn't clearly
explain the requirements of the class. Your class constructor should
*require* everything that is necessary to have a stable state when the
class is created (i.e. you should be able to properly call any function,
set any property without an exception happening)
Why? Less bugs, easier to comprehend, change/update your code. Easier to
use the class.
-W
[toc] | [prev] | [next] | [standalone]
| From | Terry Jan Reedy <tjreedy@udel.edu> |
|---|---|
| Date | 2013-05-12 16:23 -0400 |
| Message-ID | <mailman.1595.1368390250.3114.python-list@python.org> |
| In reply to | #45092 |
On 5/12/2013 12:48 PM, Wayne Werner wrote:
> I'll share the anti-pattern that I've seen many times (not actually in
> Python)
>
> class CoolPresenter:
> def __init__(self):
> self.view = None
> self.some_property = None
> self.other_property = None
>
> def initialize(self):
> self.view.disable()
> data = self.load_data()
> self.view.data = data
> self.view.enable()
>
>
> def reload(self):
> if self.view is None:
> raise NotInitializedError("Error: Please setup class")
> self.view.disable()
> data = self.load_data()
> self.view.data = data
> self.view.enable()
>
>
>
> Then you would see code like this:
>
> presenter = CoolPresenter()
> presenter.view = CoolView()
>
> This is just plain silly for a few reasons:
>
> - It's ambiguous. I don't know what's required for the CoolPresenter
> to function properly.
>
> - The temporal coupling mentioned earlier. I can create an instance of
> a class and then call a function (say `reload`) and then boom! My
> program crashes. There is *no possible* use case of this class where
> you can use it without a view.
Thank you for this examples. It makes Steven's point clearer than the
'file object' example. The problem with the latter is that objectors
could could point to file path objects, which are used to do some
manipulations of files and directory entries on the storage device
*without looking at the specific file data*. Such file data, as opposed
to directory data, never enters the program data space and with smart
enough devices, need not enter the CPU and its RAM. They could then
confuse them with file access objects which are used to transfer
specific data *between* files and program data space. The confusion is
adied by the fact that file path objects are used to create file access
objects. They do separate jobs and it seems that they are well kept
separate, even if there are languages where file path objects can have
file access functions turned on and off.
> The motivation behind this anti-pattern that I've seen is the desire to
> not have a constructor that "does too much". So you end out with an
> empty constructor and temporal coupling, and a terrible API that doesn't
> clearly explain the requirements of the class. Your class constructor
> should *require* everything that is necessary to have a stable state
> when the class is created (i.e. you should be able to properly call any
> function, set any property without an exception happening)
>
> Why? Less bugs, easier to comprehend, change/update your code. Easier to
> use the class.
--
Terry Jan Reedy
[toc] | [prev] | [next] | [standalone]
Page 3 of 5 — ← Prev page 1 2 [3] 4 5 Next page →
Back to top | Article view | comp.lang.python
csiph-web