skipping __init__ and using exploiting a class member instead

P

Peter Cacioppi

Is the following considered poor Python form?

class Foo (object) :
_lazy = None
def foo(self, x) :
_lazy = _lazy or self.get_something(x)
def get_something(self, x) :
# doesn't really matter

I like this idiom for certain situations, just wondering if it will raise the hackles of other Pythonistas.

I use this idiom sparingly, but sometimes it just fits the task at hand, I hear Guidos voice saying "use the Force" in my ear, etc.
 
N

Ned Batchelder

Is the following considered poor Python form?

class Foo (object) :
_lazy = None
def foo(self, x) :
_lazy = _lazy or self.get_something(x)
def get_something(self, x) :
# doesn't really matter

I like this idiom for certain situations, just wondering if it will raise the hackles of other Pythonistas.

I use this idiom sparingly, but sometimes it just fits the task at hand, I hear Guidos voice saying "use the Force" in my ear, etc.

You present this as a choice between __init__ or a class attribute, but
those two things are very different. Is your intent to have an instance
attribute, or a class attribute? Lazily populated instance attributes
are fine, I would do it like this:

class Foo(object):
def __init__(self):
self._lazy = None

def foo(self, x):
if self._lazy is None:
self._lazy = self.get_something(x)
...

--Ned.
 
P

Peter Cacioppi

Is the following considered poor Python form?



class Foo (object) :

_lazy = None

def foo(self, x) :

_lazy = _lazy or self.get_something(x)

def get_something(self, x) :

# doesn't really matter



I like this idiom for certain situations, just wondering if it will raisethe hackles of other Pythonistas.



I use this idiom sparingly, but sometimes it just fits the task at hand, I hear Guidos voice saying "use the Force" in my ear, etc.

To be clear, I use this when I'm subclassing something and don't need to doanything with the _lazy other than set it to None in the __init__. I like this idiom because it removes the risk of miscalling the parent __init__, and it's also quick and easy to code.

Yes there is a class member I am creating, but each instance has a distinctmember. (In other words, I'm an adult, I tested it and it works, but I couldn't find anything in the Google indicating what the collective reaction would be from Pythonistan)
 
R

Robert Kern

You present this as a choice between __init__ or a class attribute, but those
two things are very different. Is your intent to have an instance attribute, or
a class attribute? Lazily populated instance attributes are fine, I would do it
like this:

class Foo(object):
def __init__(self):
self._lazy = None

def foo(self, x):
if self._lazy is None:
self._lazy = self.get_something(x)
...

I think he left some important characters out.

class Foo (object) :
_lazy = None
def foo(self, x) :
self._lazy = self._lazy or self.get_something(x)
# Use self._lazy for something

def get_something(self, x) :
# doesn't really matter

The main difference being that he doesn't initialize the instance attribute and
just relies on the fallback to class attribute lookup. In my experience, using a
static[1] class attribute as a default for an instance attribute is accepted
practice, and one that gets touted as a positive feature of Python's namespace
model when compared against other languages. That said, I have seen it more
often in the past.

[1] In the general "unchanging" sense rather than the C++ "static" keyword sense.

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

Steven D'Aprano

Is the following considered poor Python form?

class Foo (object) :
_lazy = None
def foo(self, x) :
_lazy = _lazy or self.get_something(x)
def get_something(self, x) :
# doesn't really matter

I like this idiom for certain situations, just wondering if it will
raise the hackles of other Pythonistas.

I agree with Ned's comments -- the basic idea is fine, it's just caching
some calculated result.

I often use this idiom in read-only properties, for something that only
needs to be calculated once, but it's time-consuming to calculate and
therefore I want to delay doing the calculation until the last possible
minute. Something like this:

class Spam:
@property
def spam(self):
try:
result = self._spam
except AttributeError:
# Perform some time-consuming calculation.
result = self._spam = \
("spam "*5).capitalize() + "LOVELY SPAM!!!!!"
return result

but there are a million different variations on this theme.

I use this idiom sparingly, but sometimes it just fits the task at hand,
I hear Guidos voice saying "use the Force" in my ear, etc.

Good reasons for avoiding this pattern might include:

- the speed benefit is too trivial to justify the extra complexity;

- the risk that some condition will unexpectedly change, invalidating
the cached value, but it's too hard to notice when the cache is
invalid;

- caching the result means you unnecessarily hold on to a chunk of
memory which otherwise would be garbage collected (consider using
weak references to solve this one);

but otherwise it is unobjectionable.
 
P

Peter Cacioppi

Yes, I see the light now. My idiom works, but Steven has shown me the droids I am looking for.

Thanks!
 
P

Peter Cacioppi

To be clear, my original post had a goof.

So my original, de-goofed, idiom was


class Foo (object) :
_lazy = None
def foo(self, x) :
self._lazy = self._lazy or self.get_something(x)
def get_something(self, x) :
# doesn't really matter, so long as it returns truthy result

and the new, improved idiom is

class Foo (object) :
def foo(self, x) :
self._lazy = getattr(self, "_lazy", None) or self._get_something(x)
def _get_something(self, x) :
# doesn't really matter, so long as it returns truthy result

I was laboring under some misconception that there was Python magic that allowed __init__ and only __init__ to add class attributes by setting their values. Good to know this piece of magic isn't part of Python, and thus lazyeval can be handled more cleanly than I originally thought.

In other words, "Guido was here".

Thanks again
 
N

Ned Batchelder

To be clear, my original post had a goof.

So my original, de-goofed, idiom was


class Foo (object) :
_lazy = None
def foo(self, x) :
self._lazy = self._lazy or self.get_something(x)
def get_something(self, x) :
# doesn't really matter, so long as it returns truthy result

and the new, improved idiom is

class Foo (object) :
def foo(self, x) :
self._lazy = getattr(self, "_lazy", None) or self._get_something(x)
def _get_something(self, x) :
# doesn't really matter, so long as it returns truthy result

The use of getattr here seems unfortunate. Your original at least
didn't have that odd uncertainty about it. I'm not sure why you want to
avoid an __init__ method. Why not simply have one, and use it to
initialize your attributes, even if it is to None?

--Ned.
 
P

Peter Cacioppi

The use of getattr here seems unfortunate

Unfortunate how? It's a perfect for what I want here ... remember the context is such that the lazily stored value is always truthy (I assert this elsewhere).
I'm not sure why you want to avoid an __init__ method.

Why do you want to keep it? The more code you write the more bugs you write. Who knows, maybe I screw up the argument pass to the super __init__. Maybe I screw up the super reference. Didn't Einstein say make it as simple aspossible, but no simpler?

Personally, I find the ability of Python to subclass without overriding theconstructor very elegant. I don't believe the other languages I've worked in can do this (C++, C#, Java)... or if there is a way it's a bit scary anddiscouraged. Whereas skipping the __init__ seems to be a standard part of the Python OO development process.

Again, this is just the lazy eval pattern. In C#, for example, I'd write myconstructors but refer to _lazy only in the foo function and in it's declaration line (which would explicitly default initialize it).

At any rate, the second idiom is very pretty to me, I'm keeping it unless acompelling argument is presented. Thanks for the kibbutzing though, the first idiom was poor form, as I suspected.
 
P

Peter Cacioppi

Why not simply have one, and use it to initialize your attributes,
even if it is to None?

Think about it this way. None here really means "not yet initialized". It is a value that cannot occur naturally and thus functions as a not-initialized flag.

But for different contexts, this value could be almost anything. It might even be truthy.

So this, somewhat arbitrary, context sensitive value should be isolated as much as possible. You don't want it popping up hither and yon, you want totype as infrequently as possible and localized it to as few methods as possible.

For example, look at this version of the idiom

class Foo (Bar) :
def foo(self, x) :
if (getattr(self, "_lazy", -1) < 0 ) :
self._lazy = self._count_something(x)
assert (self._lazy >= 0) # a negative count is a bug not a bad dataentry
def count_something(self, x) :
# if it's really counting something, it will return a natural number

Now you really want that -1 in an __init__ method instead of the foo method? Isn't that asking for trouble when somebody copies this and rewrites it?They could change the boolean expressions in foo (i.e. < 0 means compute it, >= sanity checks the result) but fail to change the proper flag for not-yet-computed (-1) if these things are in two different methods.

My way, it's all in one place.

Good little idiom to ponder though. I like fussing over these things. Any good gambler develops clean habits to improve his odds, even if each act of hygiene changes the odds but slightly. I wouldn't complain if a co-worker coded this your way, but I still think it is cleaner my way.
 
R

Roy Smith

Peter Cacioppi said:
Personally, I find the ability of Python to subclass without overriding the
constructor very elegant. I don't believe the other languages I've worked in
can do this (C++, C#, Java)...

I'm not sure what point you're trying to make here. You certainly don't
have to write a constructor for a subclass in C++. This works perfectly
fine (and prints "Foo" when I run it):

#include <stdio.h>

class Foo {
public:
Foo() {
printf("Foo()\n");
};
};

class Bar : Foo {
};

int main(char**, int) {
Bar b;
}
 
M

Mark Lawrence

Personally, I find the ability of Python to subclass without overriding the constructor very elegant.

__new__ is the constructor which to my knowledge you've not mentioned,
__init__ is the initialiser as mentioned by Ben Finney.

--
Roses are red,
Violets are blue,
Most poems rhyme,
But this one doesn't.

Mark Lawrence
 
D

Devin Jeanpierre

Is the following considered poor Python form?

class Foo (object) :
_lazy = None
def foo(self, x) :
self._lazy = self._lazy or self.get_something(x)
def get_something(self, x) :
# doesn't really matter

I like this idiom for certain situations, just wondering if it will raise the hackles of other Pythonistas.

It raises my hackles, but not for any good reason.

I've seen it a number of times in the Twisted codebase, so you're not
alone in using it.

-- Devin
 
P

Peter Cacioppi

You certainly don't have to write a constructor for a subclass in C++.

Ahh, this message board is so collectively well informed (once you get past the trolls)

The C++ project I worked on was religious about always overwriting parent class constructors. I had assumed this was because the language proper forbid it, but apparently it was just project protocol.

Thanks again!
 
R

Roy Smith

You certainly don't have to write a constructor for a subclass in C++.

Ahh, this message board is so collectively well informed (once you get past
the trolls)

The C++ project I worked on was religious about always overwriting parent
class constructors. I had assumed this was because the language proper forbid
it, but apparently it was just project protocol.[/QUOTE]

One of the problems with C++ is that it's such a huge language, nobody
knows all of it. Most people learn some subset of the language that
they get comfortable with (often because that's the subset that's used
on the project they're working on). Then it's easy to assume that the
part you know is all there is to know.

Personally, I barely know the STL (because the various C++ projects I've
worked on all predated the STL and rolled their own container classes).
Likewise for most of the std streams library.
 
C

Chris Angelico

Ahh, this message board is so collectively well informed (once you get past the trolls)

The C++ project I worked on was religious about always overwriting parent class constructors. I had assumed this was because the language proper forbid it, but apparently it was just project protocol.

Minor point: In C++, you don't overwrite constructors; you simply add
your own. By the time a derived class's constructor is called, the
parents' have all already been called.

ChrisA
 
P

Peter Cacioppi

At the risk of sounding like a fogey, I actually think I did, at one time, know the distinctions between "our projects protocol" and "the language proper" for C++. I read Scott Meyers books on C++ and STL a couple of times each and helped design the protocol that kept us reasonably safe.

But this was all a long time ago, and those parts of my RAM are now storingBreaking Bad plot twists and the nuances of the Federer or Nadal debate.
 
R

Roy Smith

Peter Cacioppi said:
I read Scott Meyers books on C++ and STL a couple of times
each and helped design the protocol that kept us reasonably safe.

Scott Meyers is an incredibly smart C++ wizard. His books are amazing.
The fact that it takes somebody that smart, and books that amazing, to
teach you how not to shoot yourself in the foot with a C++ compiler says
a lot about the language.
 
P

Peter Cacioppi

That sound you here is Roy Smith hitting the nail on the head re: C++ and Scott Meyers.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,817
Latest member
DicWeils

Latest Threads

Top