Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #44944 > unrolled thread

object.enable() anti-pattern

Started bySteven D'Aprano <steve+comp.lang.python@pearwood.info>
First post2013-05-08 08:52 +0000
Last post2013-05-13 08:15 -0500
Articles 20 on this page of 82 — 23 participants

Back to article view | Back to comp.lang.python


Contents

  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 1 of 5  [1] 2 3 4 5  Next page →


#44944 — object.enable() anti-pattern

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2013-05-08 08:52 +0000
Subjectobject.enable() anti-pattern
Message-ID<518a123c$0$11094$c3e8da3@news.astraweb.com>
I'm looking for some help in finding a term, it's not Python-specific but 
does apply to some Python code.

This is an anti-pattern to avoid. The idea is that creating a resource 
ought to be the same as "turning it on", or enabling it, or similar. For 
example, we don't do this in Python:


f = file("some_file.txt")
f.open()
data = f.read()


because reading the file can fail if you forget to call open first. 
Instead, Python uses a factory function that creates the file object and 
opens it:

f = open("some_file.txt")  # if this succeeds, f is ready to use
data = f.read()


Basically, any time you have two steps required for using an object, e.g. 
like this:

obj = someobject(arg1, arg2)
obj.enable()

you should move the make-it-work functionality out of the enable method 
and into __init__ so that creating the object creates it in a state ready 
to work.

I read a blog some time ago (a year ago?) that discusses this anti-
pattern, and I'm pretty sure gave it a specific name, but I cannot find 
it again. Can anyone find any references to this anti-pattern? My google-
fu is letting me down.



(For what it's worth, I'm refactoring some of my own code that falls into 
this anti-pattern. Die, enable method, die die die!)


-- 
Steven

[toc] | [next] | [standalone]


#44946

FromChristian Heimes <christian@python.org>
Date2013-05-08 11:51 +0200
Message-ID<mailman.1446.1368006720.3114.python-list@python.org>
In reply to#44944
Am 08.05.2013 10:52, schrieb Steven D'Aprano:
> Basically, any time you have two steps required for using an object, e.g. 
> like this:
> 
> obj = someobject(arg1, arg2)
> obj.enable()
> 
> you should move the make-it-work functionality out of the enable method 
> and into __init__ so that creating the object creates it in a state ready 
> to work.

In general I agree that an object.enable() function is ugly. ;)

But it's not necessarily the best solution for all problems. If the
resource needs some kind of cleanup, the context api (__enter__() /
__exit__()) is perfectly fine way to enable and disable the object.

For example:

class MyFile:
    def __init__(self, filename):
        self.filename = filename
    def __enter__(self):
        self.open()
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()

I suggest that you mention the context API in your blog post, too.

Christian

[toc] | [prev] | [next] | [standalone]


#44948

FromRobert Kern <robert.kern@gmail.com>
Date2013-05-08 11:13 +0100
Message-ID<mailman.1448.1368008024.3114.python-list@python.org>
In reply to#44944
On 2013-05-08 09:52, Steven D'Aprano wrote:
> I'm looking for some help in finding a term, it's not Python-specific but
> does apply to some Python code.
>
> This is an anti-pattern to avoid. The idea is that creating a resource
> ought to be the same as "turning it on", or enabling it, or similar.

I don't think the anti-pattern has a name, but it's opposite pattern is named:

http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization

-- 
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]


#44951

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2013-05-08 12:30 +0000
Message-ID<518a4566$0$29997$c3e8da3$5496439d@news.astraweb.com>
In reply to#44948
On Wed, 08 May 2013 11:13:33 +0100, Robert Kern wrote:

> On 2013-05-08 09:52, Steven D'Aprano wrote:
>> I'm looking for some help in finding a term, it's not Python-specific
>> but does apply to some Python code.
>>
>> This is an anti-pattern to avoid. The idea is that creating a resource
>> ought to be the same as "turning it on", or enabling it, or similar.
> 
> I don't think the anti-pattern has a name, but it's opposite pattern is
> named:
> 
> http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization

That sounds like it should be related, but it actually isn't since RAII 
actually has little to do with *acquiring* the resource and everything to 
do with *releasing* it. See, for example:

http://stackoverflow.com/questions/2321511/what-is-meant-by-resource-
acquisition-is-initialization-raii

where it is pointed out that the resource may be acquired outside of the 
object constructor and passed in as an argument. RAII is actually about 
deterministic destruction of objects and the release of their resources.

But thanks for the attempt :-)


-- 
Steven

[toc] | [prev] | [next] | [standalone]


#44952

FromRoy Smith <roy@panix.com>
Date2013-05-08 09:17 -0400
Message-ID<roy-B0966A.09171708052013@news.panix.com>
In reply to#44944
In article <518a123c$0$11094$c3e8da3@news.astraweb.com>,
 Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote:

> I'm looking for some help in finding a term, it's not Python-specific but 
> does apply to some Python code.
> 
> This is an anti-pattern to avoid. The idea is that creating a resource 
> ought to be the same as "turning it on", or enabling it, or similar. For 
> example, we don't do this in Python:
> 
> 
> f = file("some_file.txt")
> f.open()
> data = f.read()

I've worked with C++ code that did this.  At one point in the evolution 
of OOP group consciousness, there was a feeling that constructors must 
never fail.  I don't remember if it was a general language-agnostic 
pattern, or a specific C++ reaction to poor exception handling code in 
early compilers.  What came out of that was the pattern you describe.  
All the code that could fail was factored out of the constructor into an 
"enable" method.

That being said, sometimes there are good reasons for doing this.  One 
example might be something like:

frobnicator = Frobnicator()
for file in my_file_list:
   frobnicator.munch(file)
   for line in frobnicator:
      process(line)

If creating a Frobnicator instance is very expensive, it might pay to 
create an instance once and keep reusing it on multiple files.  Here, 
munch() is your enable() method.  But, that's not quite what you were 
talking about.

[toc] | [prev] | [next] | [standalone]


#44954

FromDuncan Booth <duncan.booth@invalid.invalid>
Date2013-05-08 14:27 +0000
Message-ID<XnsA1BA9D45BE58Cduncanbooth@127.0.0.1>
In reply to#44944
Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote:

> I'm looking for some help in finding a term, it's not Python-specific
> but does apply to some Python code.
> 
> This is an anti-pattern to avoid. The idea is that creating a resource
> ought to be the same as "turning it on", or enabling it, or similar

I've come across this under the name 'two-phase construction', but as a 
desirable(!?) pattern rathern than as an anti-pattern.

In particular Symbian used it throughout as originally their C++ 
implementation didn't support exceptions. Instead they had a separate 
cleanup stack and objects that require cleanup were pushed onto that stack 
after being fully constructed but before calling the initialisation that 
required cleanup. See 
http://www.developer.nokia.com/Community/Wiki/Two-phase_construction


-- 
Duncan Booth

[toc] | [prev] | [next] | [standalone]


#44992

FromDan Sommers <dan@tombstonezero.net>
Date2013-05-09 02:38 +0000
Message-ID<M_Dit.288$oB7.229@newsfe02.iad>
In reply to#44954
On Wed, 08 May 2013 14:27:53 +0000, Duncan Booth wrote:

> Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote:

>> I'm looking for some help in finding a term, it's not Python-specific
>> but does apply to some Python code.
>> 
>> This is an anti-pattern to avoid. The idea is that creating a
>> resource ought to be the same as "turning it on", or enabling it, or
>> similar
> 
> I've come across this under the name 'two-phase construction', but as
> a desirable(!?) pattern rathern than as an anti-pattern.

Think of spinning off a thread:  initialize it synchronously, and then
let it execute asynchronously.  We tend towards that pattern even when
we know that execution will be synchronous, because we also that it
could be asynchronous one day.  Yes, we could wrap that up in a neat
bundle, but explicit is better than implicit.

Dan

[toc] | [prev] | [next] | [standalone]


#45003

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2013-05-09 05:37 +0000
Message-ID<518b361f$0$11120$c3e8da3@news.astraweb.com>
In reply to#44992
On Thu, 09 May 2013 02:38:36 +0000, Dan Sommers wrote:

> Think of spinning off a thread:  initialize it synchronously, and then
> let it execute asynchronously.  We tend towards that pattern even when
> we know that execution will be synchronous, because we also that it
> could be asynchronous one day.

Whether it is synchronous or asynchronous is irrelevant.

I can see use-cases for separating "make it go" from initialisation. It 
all depends on what you might want to do to the object before making it 
go. If the answer is "Nothing", then there is no reason not to have the 
constructor make it go. If the answer is, "Well, sometimes we need to do 
things to the object before making it go", then it makes sense to 
separate the two:

blob = MyBlob(arg1, arg2, agr3)
blob.poke("with a stick")
blob.append(data)
blob.foo = "spam"
blob.make_it_go()


I'm not talking about this case. I'm talking about the case where there's 
nothing you can do with the blob before making it go.


> Yes, we could wrap that up in a neat
> bundle, but explicit is better than implicit.

"And that is why we always write code like this:

n = int(int(len(something)) + int(1))

just to be sure that the result is explicitly an int and not just 
implicitly an int. Suppose some Javascript programmer was reading the 
code, and they thought that 1 was a floating point value. That would be 
bad!"


-- 
Steven

[toc] | [prev] | [next] | [standalone]


#45005

FromChris Angelico <rosuav@gmail.com>
Date2013-05-09 15:52 +1000
Message-ID<mailman.1479.1368078731.3114.python-list@python.org>
In reply to#45003
On Thu, May 9, 2013 at 3:37 PM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> I can see use-cases for separating "make it go" from initialisation. It
> all depends on what you might want to do to the object before making it
> go. If the answer is "Nothing", then there is no reason not to have the
> constructor make it go. If the answer is, "Well, sometimes we need to do
> things to the object before making it go", then it makes sense to
> separate the two:
>
> blob = MyBlob(arg1, arg2, agr3)
> blob.poke("with a stick")
> blob.append(data)
> blob.foo = "spam"
> blob.make_it_go()

Example use-case: Most GUI frameworks. You create a window, then
populate it, then show it. When you create the window object in
Python, you expect an actual window to exist, it should have its
handle etc. So it's still the same thing; the object is fully created
in its constructor.

ChrisA

[toc] | [prev] | [next] | [standalone]


#44997

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2013-05-09 03:12 +0000
Message-ID<518b1409$0$29997$c3e8da3$5496439d@news.astraweb.com>
In reply to#44954
On Wed, 08 May 2013 14:27:53 +0000, Duncan Booth wrote:

> Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote:
> 
>> I'm looking for some help in finding a term, it's not Python-specific
>> but does apply to some Python code.
>> 
>> This is an anti-pattern to avoid. The idea is that creating a resource
>> ought to be the same as "turning it on", or enabling it, or similar
> 
> I've come across this under the name 'two-phase construction', but as a
> desirable(!?) pattern rathern than as an anti-pattern.
> 
> In particular Symbian used it throughout as originally their C++
> implementation didn't support exceptions. Instead they had a separate
> cleanup stack and objects that require cleanup were pushed onto that
> stack after being fully constructed but before calling the
> initialisation that required cleanup. See
> http://www.developer.nokia.com/Community/Wiki/Two-phase_construction



Thanks for the link. It's obviously not the blog post I was looking for, 
but it is interesting.



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#44993

FromDan Sommers <dan@tombstonezero.net>
Date2013-05-09 02:42 +0000
Message-ID<Z1Eit.289$oB7.283@newsfe02.iad>
In reply to#44944
On Wed, 08 May 2013 08:52:12 +0000, Steven D'Aprano wrote:

> This is an anti-pattern to avoid. The idea is that creating a resource 
> ought to be the same as "turning it on", or enabling it, or similar. For 
> example, we don't do this in Python:
> 
> f = file("some_file.txt")
> f.open()
> data = f.read()

So why don't we do this?

    data = read("some_file.txt")

> because reading the file can fail if you forget to call open first. 
> Instead, Python uses a factory function that creates the file object and 
> opens it:
> 
> f = open("some_file.txt")  # if this succeeds, f is ready to use
> data = f.read()

That's just the "enable" paradigm in a socially acceptable and
traditional wrapper.

Opening and closing the file is an implementation detail, often defeated
by caching (application level or OS level) anyway.

Dan

[toc] | [prev] | [next] | [standalone]


#45002

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2013-05-09 05:23 +0000
Message-ID<518b32ef$0$11120$c3e8da3@news.astraweb.com>
In reply to#44993
On Thu, 09 May 2013 02:42:01 +0000, Dan Sommers wrote:

> On Wed, 08 May 2013 08:52:12 +0000, Steven D'Aprano wrote:
> 
>> This is an anti-pattern to avoid. The idea is that creating a resource
>> ought to be the same as "turning it on", or enabling it, or similar.
>> For example, we don't do this in Python:
>> 
>> f = file("some_file.txt")
>> f.open()
>> data = f.read()
> 
> So why don't we do this?
> 
>     data = read("some_file.txt")

Because there is a lot more that you might want to do to a file than just 
read from it.

py> f = open('/tmp/x')
py> dir(f)
['_CHUNK_SIZE', '__class__', '__delattr__', '__dict__', '__dir__', 
'__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', 
'__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', 
'__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', 
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', 
'__str__', '__subclasshook__', '_checkClosed', '_checkReadable', 
'_checkSeekable', '_checkWritable', 'buffer', 'close', 'closed', 
'detach', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 
'line_buffering', 'mode', 'name', 'newlines', 'read', 'readable', 
'readline', 'readlines', 'seek', 'seekable', 'tell', 'truncate', 
'writable', 'write', 'writelines']


That's 24 public methods and attributes, excluding private and dunder 
attributes.

There is no sensible use-case for creating a file without opening it. 
What would be the point? Any subsequent calls to just about any method 
will fail. Since you have to open the file after creating the file object 
anyway, why make them two different calls?

Besides, this is not to denigrate the idea of a read() function that 
takes a filename and returns its contents. But that is not an object 
constructor. It may construct a file object internally, but it doesn't 
return the file object, so it is completely unrelated to the scenario I 
described.



>> because reading the file can fail if you forget to call open first.
>> Instead, Python uses a factory function that creates the file object
>> and opens it:
>> 
>> f = open("some_file.txt")  # if this succeeds, f is ready to use 
>> data = f.read()
> 
> That's just the "enable" paradigm in a socially acceptable and
> traditional wrapper.

Well duh. That's like saying that for loops, while loops and co-routines 
are just GOTO in a socially acceptable and traditional wrapper. Of course 
they are. That's the whole point.



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#45008

FromTerry Jan Reedy <tjreedy@udel.edu>
Date2013-05-09 02:41 -0400
Message-ID<mailman.1481.1368081725.3114.python-list@python.org>
In reply to#45002
On 5/9/2013 1:23 AM, Steven D'Aprano wrote:

> Besides, this is not to denigrate the idea of a read() function that
> takes a filename and returns its contents. But that is not an object
> constructor. It may construct a file object internally, but it doesn't
> return the file object, so it is completely unrelated to the scenario I
> described.

At least a few stdlib modules that define classes *also* have such 
functions. They create an instance of the class, call one or more 
methods, and return the result of the method, discarding the instance. 
For instance, see the subprocess module, its POpen class, and module 
functions; or the timeit module, its Timer class, and functions.

[toc] | [prev] | [next] | [standalone]


#45014

FromGregory Ewing <greg.ewing@canterbury.ac.nz>
Date2013-05-09 19:54 +1200
Message-ID<av132gFneo2U1@mid.individual.net>
In reply to#45002
Steven D'Aprano wrote:
> There is no sensible use-case for creating a file without opening it. 
> What would be the point?

Early unix systems often used this as a form of locking.

-- 
Greg

[toc] | [prev] | [next] | [standalone]


#45019

FromCameron Simpson <cs@zip.com.au>
Date2013-05-09 18:23 +1000
Message-ID<mailman.1488.1368087823.3114.python-list@python.org>
In reply to#45014
On 09May2013 19:54, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
| Steven D'Aprano wrote:
| >There is no sensible use-case for creating a file without opening
| >it. What would be the point?
| 
| Early unix systems often used this as a form of locking.

Not just early systems: it's a nice lightweight method of making a
lockfile even today if you expect to work over NFS, where not that
many things are synchronous. 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, even over NFS.

Cheers,
-- 
Cameron Simpson <cs@zip.com.au>

You can listen to what everybody says, but the fact remains that you've got
to get out there and do the thing yourself.     - Joan Sutherland

[toc] | [prev] | [next] | [standalone]


#45027

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2013-05-09 11:30 +0000
Message-ID<518b88d8$0$29997$c3e8da3$5496439d@news.astraweb.com>
In reply to#45019
On Thu, 09 May 2013 18:23:31 +1000, Cameron Simpson wrote:

> On 09May2013 19:54, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
> | Steven D'Aprano wrote:
> | > There is no sensible use-case for creating a file WITHOUT OPENING
> | > it. What would be the point?
> |
> | Early unix systems often used this as a form of locking.
> 
> Not just early systems: it's a nice lightweight method of making a
> lockfile even today if you expect to work over NFS, where not that many
> things are synchronous. You OPEN A FILE with "0" modes

[emphasis added]

This is all very well and good, but for the life of me, I cannot see how 
opening a file is a good example of not opening a file. Perhaps it is a 
Zen thing, like the sound no spoon makes when you don't tap it against a 
glass that isn't there.


-- 
Steven

[toc] | [prev] | [next] | [standalone]


#45064

FromCameron Simpson <cs@zip.com.au>
Date2013-05-10 09:36 +1000
Message-ID<mailman.1512.1368142627.3114.python-list@python.org>
In reply to#45027
On 09May2013 11:30, Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote:
| On Thu, 09 May 2013 18:23:31 +1000, Cameron Simpson wrote:
| 
| > On 09May2013 19:54, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
| > | Steven D'Aprano wrote:
| > | > There is no sensible use-case for creating a file WITHOUT OPENING
| > | > it. What would be the point?
| > |
| > | Early unix systems often used this as a form of locking.
| > 
| > Not just early systems: it's a nice lightweight method of making a
| > lockfile even today if you expect to work over NFS, where not that many
| > things are synchronous. You OPEN A FILE with "0" modes
| 
| [emphasis added]
| This is all very well and good, but for the life of me, I cannot see how 
| opening a file is a good example of not opening a file. Perhaps it is a 
| Zen thing, like the sound no spoon makes when you don't tap it against a 
| glass that isn't there.

Because a file usually does not exist in isolation (yes sometimes
we want an isolated file). Files usually exist in the filesystem,
which is a namespace. And this is effectively a namespace operation,
not a data storage operation.

Of course, I can take this the other way: just because I opened it
with a 0 mode field doesn't mean _I_, the opener, cannot read/write
it. I've got an open file handle... A race free way to make a scratch
file in a shared area, for example.

The point is probably that a file isn't merely a feature free byte
storage container; in the real world they usually come with all
sorts of features like names and permissions. Those features will
always imply creative uses.

Anyway, this has little to do with your antipattern (about which
I'm not totally convinced anyway unless it is a rule of thumb or
code smell). It might apply to a Platonicly ideal file, but real
files have more than one use case.

Cheers,
-- 
Cameron Simpson <cs@zip.com.au>

I just kept it wide-open thinking it would correct itself.
Then I ran out of talent.       - C. Fittipaldi

[toc] | [prev] | [next] | [standalone]


#45083

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2013-05-10 05:00 +0000
Message-ID<518c7f05$0$29997$c3e8da3$5496439d@news.astraweb.com>
In reply to#45064
On Fri, 10 May 2013 09:36:43 +1000, Cameron Simpson wrote:

> On 09May2013 11:30, Steven D'Aprano
> <steve+comp.lang.python@pearwood.info> wrote: 
> | On Thu, 09 May 2013 18:23:31 +1000, Cameron Simpson wrote: 
> |
> | > On 09May2013 19:54, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
> | > | Steven D'Aprano wrote:
> | > | > There is no sensible use-case for creating a file WITHOUT
> | > | > OPENING it. What would be the point?
> | > |
> | > | Early unix systems often used this as a form of locking. 
> | >
> | > Not just early systems: it's a nice lightweight method of making a
> | > lockfile even today if you expect to work over NFS, where not that
> | > many things are synchronous. You OPEN A FILE with "0" modes 
> | [emphasis added]
>
> | This is all very well and good, but for the life of me, I cannot see
> | how opening a file is a good example of not opening a file. Perhaps it
> | is a Zen thing, like the sound no spoon makes when you don't tap it
> | against a glass that isn't there.
> 
> Because a file usually does not exist in isolation (yes sometimes we
> want an isolated file). Files usually exist in the filesystem, which is
> a namespace. And this is effectively a namespace operation, not a data
> storage operation.
>
> Of course, I can take this the other way: just because I opened it with
> a 0 mode field doesn't mean _I_, the opener, cannot read/write it. I've
> got an open file handle... A race free way to make a scratch file in a
> shared area, for example.


But you are opening the file. Therefore, it cannot possibly be an example 
of not opening the file.

Unlike Pascal, there is no way to create a C file descriptor in a closed 
state. Such a thing does not exist. If you have a file descriptor, the 
file is open. Once you close it, the file descriptor is no longer valid.

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?

(For the record, "really_open" is spelled "reset" or "rewrite" depending 
on whether you want to read or write to the file.)


> The point is probably that a file isn't merely a feature free byte
> storage container; in the real world they usually come with all sorts of
> features like names and permissions. Those features will always imply
> creative uses.

There are at least three related but separate things here.

* file objects, or their low-level equivalent, file descriptors;

* pathnames;

* files on disk, which are an abstraction for data in a particular kind 
of data structure.

They are obviously related, but they are also obviously independent. You 
can have a pathname that doesn't refer to any file on disk; you can have 
a file on disk that has been lost, and therefore is no longer accessible 
via a file name. (If you run fsck or equivalent, the file may be 
recoverable, but the name will be lost.) You can open a file object, then 
unlink it so it no longer points to a file on disk, but does still accept 
read or write calls.

The counter-examples given so far apply to pathnames or files on disk. 
They don't apply to file objects and file descriptors. I have tried 
really hard to be clear that I am talking about file objects. To the 
extent that I have failed, I am sorry.


-- 
Steven

[toc] | [prev] | [next] | [standalone]


#45085

FromRoy Smith <roy@panix.com>
Date2013-05-10 01:50 -0400
Message-ID<roy-7584FC.01500910052013@news.panix.com>
In reply to#45083
In article <518c7f05$0$29997$c3e8da3$5496439d@news.astraweb.com>,
 Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote:

> there is no way to create a C file descriptor in a closed state. Such 
> a thing does not exist. If you have a file descriptor, the file is 
> open. Once you close it, the file descriptor is no longer valid.

Of course there is.

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.

Before you protest that "it's just an int, not a file descriptor", I 
should point out that they're the same thing.  It's pretty common to do 
something like:

for (int fd = 0; fd <= MAX_FD; fd++) {
   close(fd)
}

before forking, to make sure all file descriptors are closed.

[toc] | [prev] | [next] | [standalone]


#45090

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2013-05-10 09:47 +0000
Message-ID<518cc239$0$29997$c3e8da3$5496439d@news.astraweb.com>
In reply to#45085
On Fri, 10 May 2013 01:50:09 -0400, Roy Smith wrote:

> In article <518c7f05$0$29997$c3e8da3$5496439d@news.astraweb.com>,
>  Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote:
> 
>> there is no way to create a C file descriptor in a closed state. Such a
>> thing does not exist. If you have a file descriptor, the file is open.
>> Once you close it, the file descriptor is no longer valid.
> 
> Of course there is.
> 
> 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. What you 
haven't done is create the record in the file descriptor table. You can't 
expect that read(fd) or write(fd) will work, although both should fail 
safe rather than segfault if 37 happens to not be an actual file 
descriptor.

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.)

It's a nice hack, but not one that takes away from what I'm saying.


-- 
Steven

[toc] | [prev] | [next] | [standalone]


Page 1 of 5  [1] 2 3 4 5  Next page →

Back to top | Article view | comp.lang.python


csiph-web