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 2 of 5 — ← Prev page 1 [2] 3 4 5 Next page →
| From | Roy Smith <roy@panix.com> |
|---|---|
| Date | 2013-05-10 09:22 -0400 |
| Message-ID | <roy-85A979.09223110052013@news.panix.com> |
| In reply to | #45090 |
In article <518cc239$0$29997$c3e8da3$5496439d@news.astraweb.com>,
Steven D'Aprano <steve+comp.lang.python@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.
[toc] | [prev] | [next] | [standalone]
| From | Cameron Simpson <cs@zip.com.au> |
|---|---|
| Date | 2013-05-11 08:25 +1000 |
| Message-ID | <mailman.1544.1368224768.3114.python-list@python.org> |
| In reply to | #45097 |
On 10May2013 09:22, Roy Smith <roy@panix.com> wrote:
| In article <518cc239$0$29997$c3e8da3$5496439d@news.astraweb.com>,
| Steven D'Aprano <steve+comp.lang.python@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."
[... snip ...]
I'm with Steven here.
You've made a number that can be used with calls that access the
OS file descriptor table. But it isn't a file descriptor. (Yes, the
in-program number is just a number either way.)
The descriptor table is an in-kernel data structure, filled with
file descriptors. All you have is a label that may or may not access
a file descriptor.
Anyway, we all know _what_ goes on. We're just having terminology issues.
Cheers,
--
Cameron Simpson <cs@zip.com.au>
My computer always does exactly what I tell it to do but sometimes I have
trouble finding out what it was that I told it to do.
- Dick Wexelblat <rlw@ida.org>
[toc] | [prev] | [next] | [standalone]
| From | Mark Janssen <dreamingforward@gmail.com> |
|---|---|
| Date | 2013-05-10 20:16 -0700 |
| Message-ID | <mailman.1546.1368242179.3114.python-list@python.org> |
| In reply to | #45097 |
> | 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." > [... snip ...] > > I'm with Steven here. > > You've made a number that can be used with calls that access the > OS file descriptor table. But it isn't a file descriptor. (Yes, the > in-program number is just a number either way.) Steven, don't be misled. POSIX is not the model to look to -- it does not acknowledge that files are actual objects that reside on a piece of hardware. It is not simply an integer. Mark
[toc] | [prev] | [next] | [standalone]
| From | Thomas Rachel <nutznetz-0c1b6768-bfa9-48d5-a470-7603bd3aa915@spamschutz.glglgl.de> |
|---|---|
| Date | 2013-05-11 06:21 +0200 |
| Message-ID | <kmkh0p$oe0$1@r01.glglgl.de> |
| In reply to | #45097 |
Am 10.05.2013 15:22 schrieb Roy Smith:
> 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.
Nevertheless, your 37 is not a FD yet.
Let's take your program:
> #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);
Until here, the numbers 3 toll max_files may or may not be FDs.
> for (int i = 3; i < max_files; ++i) {
> close(i);
> }
Now they are closed; they are definitely no longer FDs even if they
were. If you would use them in a file operation, you'd get a EBADF which
means "fd is not a valid file descriptor".
> dup(2);
From now on, 3 is a FD and you can use it as such.
> char* message = "hello, fd world\n";
> write(3, message, strlen(message));
> }
>
> No, what I've done is taken advantage of behaviors which are guaranteed
> by POSIX.
Maybe, but the integer numbers get or los their property as a file
descriptor with open() and close() and not by assigning them to an int.
Thomas
[toc] | [prev] | [next] | [standalone]
| From | Mark Janssen <dreamingforward@gmail.com> |
|---|---|
| Date | 2013-05-10 21:00 -0700 |
| Message-ID | <mailman.1555.1368273954.3114.python-list@python.org> |
| In reply to | #45097 |
> Steven, don't be misled. POSIX is not the model to look to -- it does > not acknowledge that files are actual objects that reside on a piece > of hardware. It is not simply an integer. Please disregard this (my own) flame bait. -- MarkJ Tacoma, Washington
[toc] | [prev] | [next] | [standalone]
| From | Dennis Lee Bieber <wlfraed@ix.netcom.com> |
|---|---|
| Date | 2013-05-11 15:04 -0400 |
| Message-ID | <mailman.1560.1368299090.3114.python-list@python.org> |
| In reply to | #45083 |
On 10 May 2013 05:00:53 GMT, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> declaimed the following in
gmane.comp.python.general:
>
> But even if C allowed you to do so, doesn't mean that it is a good idea.
> At least some variants of Pascal force you to do the following:
>
> # Pseudo-code.
> f = open(filename)
> really_open(f)
> data = read(f) # or write, or *any other operation at all*
>
> Surely we can agree that having to call both open() and really_open()
> before you get an actually opened file that you can use is one call too
> many? There is *nothing* you can do with f before calling really_open().
> So why require it?
>
Well... As I recall, original Pascal required all files to be named
on the command line invocation. There was no way to dynamically assign a
file name to a file object within the language.
{Looking at the 3rd Edition Jensen&Wirth confirms that -- even the ISO
standard Pascal does not have an internal way to link a filename to a
file variable.}
> (For the record, "really_open" is spelled "reset" or "rewrite" depending
> on whether you want to read or write to the file.)
So your example, the open could better be names "assign" or "link",
as it associates the name of the file with the file variable, but does
not do anything with the file system itself -- that would be the
standard Pascal reset/rewrite.
A concept that is probably an artifact of the mainframe operating
systems in those days. Linkage of a physical file to a languages
internal "file" concept tended to be done with job control language
surrounding the invocation of the program itself.
--
Wulfraed Dennis Lee Bieber AF6VN
wlfraed@ix.netcom.com HTTP://wlfraed.home.netcom.com/
[toc] | [prev] | [next] | [standalone]
| From | Greg Ewing <greg.ewing@canterbury.ac.nz> |
|---|---|
| Date | 2013-05-10 10:56 +1200 |
| Message-ID | <mailman.1509.1368140219.3114.python-list@python.org> |
| In reply to | #45014 |
Cameron Simpson wrote: > You open a file with "0" modes, so > that it is _immediately_ not writable. Other attempts to make the > lock file thus fail because of the lack of write, I don't think that's quite right. You open it with O_CREAT+O_EXCL, which atomically fails if the file already exists. The read/write modes don't really come into it, as far as I know. -- Greg
[toc] | [prev] | [next] | [standalone]
| From | Roy Smith <roy@panix.com> |
|---|---|
| Date | 2013-05-09 09:07 -0400 |
| Message-ID | <roy-8428A6.09074209052013@news.panix.com> |
| In reply to | #45002 |
In article <518b32ef$0$11120$c3e8da3@news.astraweb.com>, Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote: > There is no sensible use-case for creating a file without opening it. Sure there is. Sometimes just creating the name in the file system is all you want to do. That's why, for example, the unix "touch" command exists.
[toc] | [prev] | [next] | [standalone]
| From | Oscar Benjamin <oscar.j.benjamin@gmail.com> |
|---|---|
| Date | 2013-05-09 14:51 +0100 |
| Message-ID | <mailman.1492.1368107499.3114.python-list@python.org> |
| In reply to | #45032 |
On 9 May 2013 14:07, Roy Smith <roy@panix.com> wrote: > In article <518b32ef$0$11120$c3e8da3@news.astraweb.com>, > Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote: > >> There is no sensible use-case for creating a file without opening it. > > Sure there is. Sometimes just creating the name in the file system is > all you want to do. That's why, for example, the unix "touch" command > exists. Wouldn't the code that implements the touch command just look something like this: f = open(filename) f.close() Or is there some other way of creating the file that doesn't open it (I mean in general not just in Python)? Oscar
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2013-05-09 18:21 +0000 |
| Message-ID | <518be931$0$29997$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #45032 |
On Thu, 09 May 2013 09:07:42 -0400, Roy Smith wrote: > In article <518b32ef$0$11120$c3e8da3@news.astraweb.com>, > Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote: > >> There is no sensible use-case for creating a file without opening it. > > Sure there is. Sometimes just creating the name in the file system is > all you want to do. That's why, for example, the unix "touch" command > exists. Since I neglected to make it clear above that I was still talking about file objects, rather than files on disk, I take responsibility for this misunderstanding. I thought that since I kept talking about file *objects* and *constructors*, people would understand that I was talking about in-memory objects rather than on-disk files. Mea culpa. So, let me rephrase that sentence, and hopefully clear up any further misunderstandings. There is no sensible use-case for creating a file OBJECT unless it initially wraps an open file pointer. This principle doesn't just apply to OOP languages. The standard C I/O library doesn't support creating a file descriptor unless it is a file descriptor to an open file. open() has the semantics: "It shall create an open file description that refers to a file and a file descriptor that refers to that open file description." http://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html and there is no corresponding function to create a *closed* file description. (Because such a thing would be pointless.) Of course language designers are free to design their language to work under whatever anti-patterns they desire. I quote from the Pascal Language Reference: "open associates the permanent file file [sic] with a file variable for reading or writing. open does not actually open the file; you must call reset or rewrite before reading or writing to that file." http://www.amath.unc.edu/sysadmin/DOC4.0/pascal/lang_ref/ ref_builtin.doc.html but since that's not a part of standard Pascal, other Pascals may behave differently. -- Steven
[toc] | [prev] | [next] | [standalone]
| From | MRAB <python@mrabarnett.plus.com> |
|---|---|
| Date | 2013-05-09 19:34 +0100 |
| Message-ID | <mailman.1501.1368124461.3114.python-list@python.org> |
| In reply to | #45051 |
On 09/05/2013 19:21, Steven D'Aprano wrote:
> On Thu, 09 May 2013 09:07:42 -0400, Roy Smith wrote:
>
>> In article <518b32ef$0$11120$c3e8da3@news.astraweb.com>,
>> Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote:
>>
>>> There is no sensible use-case for creating a file without opening it.
>>
>> Sure there is. Sometimes just creating the name in the file system is
>> all you want to do. That's why, for example, the unix "touch" command
>> exists.
>
> Since I neglected to make it clear above that I was still talking about
> file objects, rather than files on disk, I take responsibility for this
> misunderstanding. I thought that since I kept talking about file
> *objects* and *constructors*, people would understand that I was talking
> about in-memory objects rather than on-disk files. Mea culpa.
>
> So, let me rephrase that sentence, and hopefully clear up any further
> misunderstandings.
>
> There is no sensible use-case for creating a file OBJECT unless it
> initially wraps an open file pointer.
>
You might want to do this:
f = File(path)
if f.exists():
...
This would be an alternative to:
if os.path.exists(path):
...
> This principle doesn't just apply to OOP languages. The standard C I/O
> library doesn't support creating a file descriptor unless it is a file
> descriptor to an open file. open() has the semantics:
>
> "It shall create an open file description that refers to a file and a
> file descriptor that refers to that open file description."
>
> http://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html
>
> and there is no corresponding function to create a *closed* file
> description. (Because such a thing would be pointless.)
>
[snip]
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2013-05-10 02:30 +0000 |
| Message-ID | <518c5bbc$0$29997$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #45053 |
On Thu, 09 May 2013 19:34:25 +0100, MRAB wrote: >> There is no sensible use-case for creating a file OBJECT unless it >> initially wraps an open file pointer. >> > You might want to do this: > > f = File(path) > if f.exists(): > ... > > This would be an alternative to: > > if os.path.exists(path): > ... Sure, but your made-up File object does not represent a file, it represents a pathname which *may or may not* exist. Pathnames are not files. Not all files have a pathname that refers to them, and not all pathnames point to an actual file. Since it has an exists() method that can return False, there are so-called "File" objects that aren't files and the name is a misnomer. A much better name for the class would be "Path", not "File". I'm not saying that there is never a use-case for some objects to have an "enable" or "start" or "open" method. That would clearly be a silly thing to say. In the real world, we design many objects to have a start switch. It would be pretty horrible if your car was running all the time. But that's because there are actual use-cases for having cars *not* run, and "make it stop" is the safe default behaviour. Your fridge, on the other hand, doesn't have a "make it go" button. So long as power is applied to it, your fridge automatically runs. Likewise, your watch is an "always on" device, provided it hasn't wound down or have a flat battery. Your fire alarm is "always on". I must admit I am astonished at how controversial the opinion "if your object is useless until you call 'start', you should automatically call 'start' when the object is created" has turned out to be. -- Steven
[toc] | [prev] | [next] | [standalone]
| From | Roy Smith <roy@panix.com> |
|---|---|
| Date | 2013-05-09 23:09 -0400 |
| Message-ID | <roy-6D8B98.23095509052013@news.panix.com> |
| In reply to | #45073 |
In article <518c5bbc$0$29997$c3e8da3$5496439d@news.astraweb.com>, Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote: > I must admit I am astonished at how controversial the opinion "if your > object is useless until you call 'start', you should automatically call > 'start' when the object is created" has turned out to be. I'm sorry. I thought you were here for an argument. I think where things went pear shaped is when you made the statement: >> There is no sensible use-case for creating a file OBJECT unless it >> initially wraps an open file pointer. That's a pretty absolute point of view. Life is rarely so absolute.
[toc] | [prev] | [next] | [standalone]
| From | Mark Janssen <dreamingforward@gmail.com> |
|---|---|
| Date | 2013-05-09 20:19 -0700 |
| Message-ID | <mailman.1520.1368155947.3114.python-list@python.org> |
| In reply to | #45075 |
> I think where things went pear shaped is when you made the statement: > >>> There is no sensible use-case for creating a file OBJECT unless it >>> initially wraps an open file pointer. > > That's a pretty absolute point of view. Life is rarely so absolute. In the old days, it was useful to have fine-grained control over the file object because you didn't know where it might fail, and the OS didn't necessarily give you give good status codes. So being able to step through the entire process was the job of the progammers. Now, with languages so high like python and hardware so common, it almost is never necessary, so he has some point. A closed file pointer is useful from a OS-progamming point-of-view though because you generally never want to leave files open where they'd block other I/O. -- MarkJ Tacoma, Washington
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2013-05-10 13:46 +1000 |
| Message-ID | <mailman.1521.1368157593.3114.python-list@python.org> |
| In reply to | #45075 |
On Fri, May 10, 2013 at 1:19 PM, Mark Janssen <dreamingforward@gmail.com> wrote: >> I think where things went pear shaped is when you made the statement: >> >>>> There is no sensible use-case for creating a file OBJECT unless it >>>> initially wraps an open file pointer. >> >> That's a pretty absolute point of view. Life is rarely so absolute. > > In the old days, it was useful to have fine-grained control over the > file object because you didn't know where it might fail, and the OS > didn't necessarily give you give good status codes. So being able to > step through the entire process was the job of the progammers. I don't know what you mean by the "old days", but a couple of decades ago, there were no such things as "file objects". You call a function to open a file, you get back a number. You explicitly close that by calling another function and passing it that number. In fact, there is no way to have a "file object" that doesn't have an open file associated with it, because it's simply... a number. > Now, with languages so high like python and hardware so common, it > almost is never necessary, so he has some point. A closed file > pointer is useful from a OS-progamming point-of-view though because > you generally never want to leave files open where they'd block other > I/O. I'm beginning to wonder if you and Dihedral are swapping notes. Dihedral's been sounding fairly coherent lately. ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2013-05-10 05:03 +0000 |
| Message-ID | <518c7f8e$0$29997$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #45075 |
On Thu, 09 May 2013 23:09:55 -0400, Roy Smith wrote: > In article <518c5bbc$0$29997$c3e8da3$5496439d@news.astraweb.com>, > Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote: > >> I must admit I am astonished at how controversial the opinion "if your >> object is useless until you call 'start', you should automatically call >> 'start' when the object is created" has turned out to be. > > I'm sorry. I thought you were here for an argument. No, I'm here for the abuse. > I think where things went pear shaped is when you made the statement: > >>> There is no sensible use-case for creating a file OBJECT unless it >>> initially wraps an open file pointer. > > That's a pretty absolute point of view. Life is rarely so absolute. So far the only counter-examples given aren't counter-examples. One involves opening the file. The other involves something which isn't a file, but a string instead. If there are any counter-examples, they are impossible in Python and C: you cannot create a file object in Python without opening it, and you cannot create a file descriptor in C without opening it. But not in Pascal, which actually supports my claim that this is an anti-pattern: while some Pascal implementations do allow you to create a non-open file, you cannot do *anything* with it until you open it, except generate bugs. -- Steven
[toc] | [prev] | [next] | [standalone]
| From | Dan Sommers <dan@tombstonezero.net> |
|---|---|
| Date | 2013-05-10 06:22 +0000 |
| Message-ID | <Hm0jt.6$I26.5@newsfe26.iad> |
| In reply to | #45084 |
On Fri, 10 May 2013 05:03:10 +0000, Steven D'Aprano wrote: >>>> 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? I suppose that this case exists even in a non-distributed system that allows whatever execution unit exists to change its own security settings (POSIX has setuid and setgid functions), or exposes file ACLs as methods of file objects rather than of the OS or file system. What, exactly, does a "file object" represent? And going back to your original comment (which was actually in response to one of my posts), at least some operations on python file objects *could* succeed without having to open the file. An OS could provide truncate, or writeable, on un-opened files; and certainly Python could provide encoding, or isatty, on un-opened files. Of these, truncate might be the closest use case of creating a file object without any intent to write to it, for some definition of "write to it." Dan [0] Yes, I understand that asking first instead of trying to delete the file is just asking to lose any number of potential race conditions, assuming that your system even supports race conditions. [1] Think about a multi-threaded (or otherwise distributed) FTP or HTTP server. No, don't think about the server vs. the client, but rather a "core" server overseeing "sub" servers for different sessions.
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2013-05-10 11:00 +0000 |
| Message-ID | <518cd360$0$29997$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #45086 |
On Fri, 10 May 2013 06:22:31 +0000, Dan Sommers wrote:
> On Fri, 10 May 2013 05:03:10 +0000, Steven D'Aprano wrote:
>
>>>>> 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.
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()
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? If you don't have a good, reasonable, non-
contrived use-case for a separate "make it go" method, don't use one.
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:
# Do this:
count = 0
count += 1
# Not this:
count = []
count.insert(0, [])
*wink*
--
Steven
[toc] | [prev] | [next] | [standalone]
| From | Robert Kern <robert.kern@gmail.com> |
|---|---|
| Date | 2013-05-10 13:19 +0100 |
| Message-ID | <mailman.1527.1368188358.3114.python-list@python.org> |
| In reply to | #45092 |
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. To artificially limit the discussion of the anti-pattern to the trivial, entirely unmotivated case forbids most of the interesting and instructive parts of the conversation. -- 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 | Roy Smith <roy@panix.com> |
|---|---|
| Date | 2013-05-10 10:01 -0400 |
| Message-ID | <roy-F6FC44.10011410052013@news.panix.com> |
| In reply to | #45093 |
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. 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".
[toc] | [prev] | [next] | [standalone]
Page 2 of 5 — ← Prev page 1 [2] 3 4 5 Next page →
Back to top | Article view | comp.lang.python
csiph-web