object.enable() anti-pattern

Roy Smith roy at panix.com
Fri May 10 09:22:31 EDT 2013


In article <518cc239$0$29997$c3e8da3$5496439d at news.astraweb.com>,
 Steven D'Aprano <steve+comp.lang.python at pearwood.info> wrote:

> > int fd = 37;
> > 
> > I've just created a file descriptor.  There is not enough information
> > given to know if it corresponds to an open file or not.
> 
> No, you haven't created a file descriptor. You've made up a number which 
> C will allow you to use as an index into the file descriptor table, 
> because C is a high-level assembler with very little in the way of type 
> safety, and what little there is you can normally bypass.

No, I've created a file descriptor, which is, by definition, an integer. 
It has nothing to do with C.  This is all defined by the POSIX 
interface.  For example, the getdtablesize(2) man page says:

"The entries in the descriptor table are numbered with small integers 
starting at 0.  The call getdtablesize() returns the size of this table."

So, I am now guaranteed that fds will be ints.  I also know the 
guaranteed minimum and maximum values.

The system even makes certain guarantees which let me predict what file 
descriptor I'll get next in certain situations.  For example, from the 
dup(2) page on my OSX box:

"The new descriptor returned by the call is the lowest numbered 
descriptor currently not in use by the process."

> What you haven't done is create the record in the file descriptor table.

That's correct.  But, as described above, the system makes certain 
guarantees which allow me to reason about the existence or non-existence 
os such entries.

> You can't expect that read(fd) or write(fd) will work

I can expect that they will work if I have reasoned correctly about the 
POSIX-guaranteed semantics.  For example, POSIX says(*) that this C 
program is guaranteed to print, "hello, fd world" (assuming the 
assertion passes):

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

int main(int argc, char** argv) {
    int max_files = getdtablesize();
    assert(max_files >= 4);

    for (int i = 3; i < max_files; ++i) {
        close(i);
    }

    dup(2);
    char* message = "hello, fd world\n";
    write(3, message, strlen(message));
}

> What you've done is the moral equivalent of choosing an integer at 
> random, coercing it to a pointer, then dereferencing it to peek or poke 
> at some memory address. (Although fortunately much safer.)

No, what I've done is taken advantage of behaviors which are guaranteed 
by POSIX.

But, we're going off into the weeds here.  Where this started was you 
said:

> There is no sensible use-case for creating a file WITHOUT OPENING
> it. What would be the point?

I agree with you, in general, that it is usually poor design to have 
classes which require instances to be initialized after they are created.

The problem is, you chose as your example a particular domain where the 
underlying objects being modeled have unusual semantics imposed by an 
interface that's 40 years old.  And then you made absolute statements 
about there not possibly ever being certain use cases, when clearly 
there are (for that domain).

-----------------------
(*) Technically, getdtablesize() isn't POSIX, but POSIX does define 
other ways to get the same information.



More information about the Python-list mailing list