one-time factory in python for an experienced java guy

P

phonky

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

Stefan Behnel

phonky said:
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
 
P

Paul Rubin

Stefan Behnel said:
Use a global variable in the module.

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()
 
P

phonky

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

Paul Rubin

phonky said:
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.
 
P

phonky

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?
 
P

Paul Rubin

phonky said:
"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
 
P

Peter Otten

phonky said:
Thanks Paul,


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?

In that case you may want to stick with the class attribute:
.... def __init__(self):
.... self.account = self.next_account()
.... def __str__(self):
.... return "Account(number=%r)" % self.account
.... __repr__ = __str__
....(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
 
A

Aahz

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

pdpi

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

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

phonky

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!
 
J

Jonathan Gardner

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.
 

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,755
Messages
2,569,537
Members
45,021
Latest member
AkilahJaim

Latest Threads

Top