Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

one-time factory in python for an experienced java guy

9 views
Skip to first unread message

phonky

unread,
Jul 14, 2009, 10:03:16 AM7/14/09
to
Hi

I have searched all over and haven't found the solution
for my problem yet. I am new to python, and all the time realize I
do program python in java, which is not great.

Besides being a real-life problem, I want to
solve it as elegant as I can, using it to
also learn about python (I know I could just
hack something easy for now).

That's what I want to do.

I have an Account class.
An Account instance has to get an account number.
On instance creation, I want the account number to
be generated (I don't want callers to pass
the account # to ensure uniqueness):

class Account(object):
def __init__(self, holder):
self.__accountnumber = self.__generate_account_number()

Now, I do not know yet how the account number scheme looks like.
For now, I just want to have an incremental number; later,
when going to production, we'll need the proper one.

Furthermore, as we plan to distribute the package, we want
to allow custom account numbering schemes. Thus, customers
should be able to plug in their own AccountNumberGenerator implementation.

For now, I have a generators.py module.

I have an AccountNumberGenerator base class,
all subclasses should implement "generate".

I have an IncrementalGenerator subclass.

So for now, I need to instantiate IncrementalGenerator,
allowing for a future module to plugin their own generator.

Any suggestions on how to do this?
Thanks so much!!!!

Stefan Behnel

unread,
Jul 14, 2009, 10:27:48 AM7/14/09
to
phonky wrote:
> class Account(object):
> def __init__(self, holder):
> self.__accountnumber = self.__generate_account_number()
>
> Now, I do not know yet how the account number scheme looks like.
> For now, I just want to have an incremental number; later,
> when going to production, we'll need the proper one.

Use a global variable in the module.

Stefan

Paul Rubin

unread,
Jul 14, 2009, 10:33:26 AM7/14/09
to
Stefan Behnel <stef...@behnel.de> writes:

Yuch! Don't use a global. Try something like:

import itertools

class Account(object):
def __init__(self, holder, gen=itertools.count()):
self.__accountnumber = gen.next()

phonky

unread,
Jul 14, 2009, 10:34:30 AM7/14/09
to
Stefan, thanks first of all

> Use a global variable in the module.

I have an account_number_generator variable in the module,
I got that hint from searching the web.

But where my stubborn java mind doesn't release me:
what does the variable contain? Do I create the actual
IncrementalGenerator object there? Or the super class?
Or just a string, which a factory method takes to
create the actual object?

What I especially don't get:
How will an external module overwrite that variable?

I fail to see, python being interpreted, how I can ensure
that a future module is being executed later, thus
overwriting the variable.

Thanks again.

Paul Rubin

unread,
Jul 14, 2009, 10:40:52 AM7/14/09
to
phonky <pho...@europe.com> writes:
> But where my stubborn java mind doesn't release me: what does the
> variable contain? Do I create the actual IncrementalGenerator object
> there? Or the super class? Or just a string, which a factory method
> takes to create the actual object?

Ugh, just forget everything you ever knew about java. Do some Zen
exercises to erase your mind. Then read a Python tutorial as if
you're starting from nothing.

phonky

unread,
Jul 14, 2009, 10:55:56 AM7/14/09
to
Thanks Paul,

> Ugh, just forget everything you ever knew about java. Do some Zen
> exercises to erase your mind. Then read a Python tutorial as if
> you're starting from nothing.

Yeah, surely right, but easier said than done...
I'm working on it.

Taking your example.

import itertools

class Account(object):
def __init__(self, holder, gen=itertools.count()):
self.__accountnumber = gen.next()

If you consider my python illiteracy,

"itertools.count(): Make an iterator that returns consecutive integers
starting with n"

to me that sounds like that solves the increment issue, but what about
future modules wanting to plug in a different
numbering format, e.g. 205434.1234 or whatever?


Paul Rubin

unread,
Jul 14, 2009, 11:14:18 AM7/14/09
to
phonky <pho...@europe.com> writes:
> "itertools.count(): Make an iterator that returns consecutive integers
> starting with n"
>
> to me that sounds like that solves the increment issue, but what about
> future modules wanting to plug in a different
> numbering format, e.g. 205434.1234 or whatever?

You'd write a different generator for that, and use it instead of
itertools.count. E.g. (untested):

def different_gen():
a = 201593.0768 # initial number
while True:
yield '%.04f'% a
a += 123.4567 # increase in increments of this much

Peter Otten

unread,
Jul 14, 2009, 11:20:03 AM7/14/09
to
phonky wrote:

In that case you may want to stick with the class attribute:

>>> class Account(object):
... def __init__(self):
... self.account = self.next_account()
... def __str__(self):
... return "Account(number=%r)" % self.account
... __repr__ = __str__
...
>>> from itertools import count
>>> Account.next_account = count(42).next
>>> a = Account()
>>> b = Account()
>>> a, b
(Account(number=42), Account(number=43))
>>> from uuid import uuid1
>>> Account.next_account = staticmethod(uuid1)
>>> c = Account()
>>> d = Account()
>>> c, d
(Account(number=UUID('b0f8dfc6-7087-11de-be16-001d923f29c5')),
Account(number=UUID('b310c90e-7087-11de-be16-001d923f29c5')))

You can plug in arbitrary callables at runtime. The only complication I can
see is that you may have to wrap them into a staticmethod to prevent python
from passing the self reference. You can avoid that if you just use a global
instead:

# account.py
next_account = ...
class Account(object):
def __init__(self): self.number = next_account()

You can then set the factory elsewhere

# main.py
import account
account.next_account = ...
a = Account()

In general in python we like to keep simple things simple rather than
creating a huge bureaucracy.

Peter

Aahz

unread,
Jul 14, 2009, 11:24:14 AM7/14/09
to
In article <23406$4a5c9c7d$d9a2f023$27...@news.hispeed.ch>,

phonky <pho...@europe.com> wrote:
>
>import itertools
>
> class Account(object):
> def __init__(self, holder, gen=itertools.count()):
> self.__accountnumber = gen.next()
>
>If you consider my python illiteracy,
>
>"itertools.count(): Make an iterator that returns consecutive integers
>starting with n"
>
>to me that sounds like that solves the increment issue, but what about
>future modules wanting to plug in a different
>numbering format, e.g. 205434.1234 or whatever?

Here's what I would do:

class Account:
gen = itertools.count
gen_instance = None

def __init__(self, holder):
if self.gen_instance is None:
self.__class__.gen_instance = self.gen()
self._account = self.gen_instance()

Notice that I'm using only a single underscore for ``_account`` to make
inheritance simpler, and I'm using ``self`` to access class attributes
*except* when I need to *set* the class attribute, which requires
``self.__class__``.

Now anyone who wants to change the generator can simply do
module.Account.gen = other_gen
and it will work as long as no Account() instances have been created (you
don't want the generator to change mid-stream, right?).
--
Aahz (aa...@pythoncraft.com) <*> http://www.pythoncraft.com/

"If you think it's expensive to hire a professional to do the job, wait
until you hire an amateur." --Red Adair

pdpi

unread,
Jul 14, 2009, 11:35:40 AM7/14/09
to

You don't want an AccountNumberGenerator class and subclassing, all
you need is an iterator/generator of some form.

These might help:
http://docs.python.org/tutorial/classes.html#iterators
http://docs.python.org/tutorial/classes.html#generators
(in fact, that whole page is pretty relevant)

Once your code expects one of those, it's trivially easy to plug
something else in there.

phonky

unread,
Jul 14, 2009, 1:50:54 PM7/14/09
to
Thanks for all replies.

I need to practice much more pythonese....
In fact I don't think to understand all
of your suggestions, so I'll need to
go through them and decide what approach I am going
to take.

Thanks a lot!

Jonathan Gardner

unread,
Jul 14, 2009, 4:05:48 PM7/14/09
to
On Jul 14, 7:03 am, phonky <pho...@europe.com> wrote:
>
> Now, I do not know yet how the account number scheme looks like.

Exactly. The data store knows a lot more than the client (your
program) will ever know.

The correct answer is to do nothing. Use your data store to generate
the IDs for you. The implementations discussed here will not generate
unique IDs across invocations, but the data store will persist the
sequence for you appropriately.

The more correct answer is to abstract away the client to your data
store as well. See SQLAlchemy.

If you're writing a data store, you're doing it wrong. See PostgreSQL,
MySQL, or any other data store out there that are perfectly fine for
development and production use.

I like to use UUIDs for the IDs. Others like big ints that are a
sequence. I've seen people use encrypted big ints, basically random
strings that aren't really random. In the end, you only have to change
the code that talks to the data store, and the rest of your program
only cares about the equality of the id.

0 new messages