setattr question

  • Thread starter Gerard Flanagan
  • Start date
G

Gerard Flanagan

Hello

I have the following code:

#### builder.py #########
class HtmlBuilder(object):

@staticmethod
def page(title=''):
return HtmlPage(title)

@staticmethod
def element(tag, text=None, **attribs):
return HtmlElement(tag, text, **attribs)

@staticmethod
def literal(text):
return HtmlLiteral(text)

class HtmlElementFactory(object):

def __init__(self):
for tag in ['li', 'ul']:
setattr( self, tag, HtmlBuilder.element(tag) )

#########################

and so I can do the following:

html = HtmlElementFactory()
ul = html.ul
ul.attrib['class'] = 'default'
for i in range(3):
li = html.li
li.text = 'ghfhj'
ul.append(li)
print ul.to_string()

but what I'd like to do is:

html = HtmlElementFactory()
ul = html.ul( class='default' )
for i in range(3):
ul.append( html.li( 'ghfhj' )
print ul.to_string()

ie. to pass along *args and **kwargs to the HtmlElement constructor.
Any suggestions? Or is there a better way to this kind of thing?

thanks

Gerard
 
B

bruno at modulix

Gerard said:
Hello

I have the following code:

#### builder.py #########
class HtmlBuilder(object):

@staticmethod
def page(title=''):
return HtmlPage(title)

@staticmethod
def element(tag, text=None, **attribs):
return HtmlElement(tag, text, **attribs)

@staticmethod
def literal(text):
return HtmlLiteral(text)

Je ne vois pas très bien à quoi sert cette classe (à moins bien sûr
qu'il y ait d'autre code). Pour ce que je vois là, pourquoi ne pas
appeler directement les classes HtmlPage, HtmlElement et HtmlLiteral ?

Err... I don't see the point of this class. Why not just calling the
HtmlPage|Element|Literal classes directly ?
class HtmlElementFactory(object):

def __init__(self):
for tag in ['li', 'ul']:
setattr( self, tag, HtmlBuilder.element(tag) )

#########################

and so I can do the following:

html = HtmlElementFactory()
ul = html.ul
ul.attrib['class'] = 'default'
for i in range(3):
li = html.li
li.text = 'ghfhj'
ul.append(li)
print ul.to_string()

but what I'd like to do is:

html = HtmlElementFactory()
ul = html.ul( class='default' )


for i in range(3):
ul.append( html.li( 'ghfhj' )
print ul.to_string()

'to_string' ?
Let's see... 'to_string', a class with only staticmethods in it...
You're coming from Java, aren't you ?-)

(if yes, google for "python is not java", it may be helpful)
ie. to pass along *args and **kwargs to the HtmlElement constructor.
Any suggestions?

yes : pass along *args and **kwargs to the HtmlElement constructor !-)

Or is there a better way to this kind of thing?

yes again : kiss (Keep It Simple Stupid)

There's not enough code to really grasp what you're trying to do, but
from what I see, I'd say you're having a bad case of arbitrary
overcomplexification.

What's wrong with:

# nb: class is a reserved word in Python
ul = HtmlElement('ul', css_class='default')
for i in range(3):
ul.append(HtmlElement('li', 'baaz%d' % i)

# nb2: use the __str__() method of HtmlElement
print str(ul)

Python's philosophy is to make simple things simple (don't worry,
there's still space for complex things -> descriptors, metaclasses etc).

HTH
 
F

Fredrik Lundh

Gerard said:
I have the following code:

#### builder.py #########
class HtmlBuilder(object):

@staticmethod
def page(title=''):
return HtmlPage(title)

@staticmethod
def element(tag, text=None, **attribs):
return HtmlElement(tag, text, **attribs)

@staticmethod
def literal(text):
return HtmlLiteral(text)

just curious, but what's the purpose of this class, and what gave
you the idea to structure your program in this way ?

</F>
 
G

Gerard Flanagan

bruno said:
Je ne vois pas très bien à quoi sert cette classe (à moins bien sûr
qu'il y ait d'autre code). Pour ce que je vois là, pourquoi ne pas
appeler directement les classes HtmlPage, HtmlElement et HtmlLiteral ?

C'est une vaine tentative d'appliquer "the Factory Pattern" ! J'ai eu
commence d'ecrire les classes 'ul(HtmlElement), li(HtmlElement), etc ',
et le but de 'HtmlElementFactory' etait d'eviter ceci (cela?). Il y a
une recette ici:

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/86900

mais il utilise 'apply', qui est...blah

I was trying to implement the factory pattern.
The recipe above uses 'apply' which is deprecated according to the
docs, and I suppose I was curious how to do the same sort of thing
without 'apply'.
Err... I don't see the point of this class. Why not just calling the
HtmlPage|Element|Literal classes directly ?
class HtmlElementFactory(object):

def __init__(self):
for tag in ['li', 'ul']:
setattr( self, tag, HtmlBuilder.element(tag) )

#########################

and so I can do the following:

html = HtmlElementFactory()
ul = html.ul
ul.attrib['class'] = 'default'
for i in range(3):
li = html.li
li.text = 'ghfhj'
ul.append(li)
print ul.to_string()

but what I'd like to do is:

html = HtmlElementFactory()
ul = html.ul( class='default' )


for i in range(3):
ul.append( html.li( 'ghfhj' )
print ul.to_string()

'to_string' ?
Let's see... 'to_string', a class with only staticmethods in it...
You're coming from Java, aren't you ?-)

Never been to Java in my life!
I don't know if I understand you, HtmlElement has a 'to_string' method
but no static methods:

class HtmlElement(list):

def __init__(self, tag, text=None, **attrib):
self.tag = tag
self.text = text
self.attrib = attrib

def write(self, writer):
writer.start(self.tag, self.attrib)
if self.text is not None:
writer.data(self.text)
for node in self:
node.write(writer)
writer.end()

def to_string(self):
out = StringIO()
writer = HtmlWriter(out)
self.write(writer)
ret = out.getvalue()
out.close()
return ret
(if yes, google for "python is not java", it may be helpful)


yes : pass along *args and **kwargs to the HtmlElement constructor !-)



yes again : kiss (Keep It Simple Stupid)

There's not enough code to really grasp what you're trying to do, but
from what I see, I'd say you're having a bad case of arbitrary
overcomplexification.

My code itself is just a learning project ( etant sans emploi a ce
moment, moi-meme...) and it is no doubt a bit 'over-egged' at the
minute, but it is a first attempt and I can always refactor later.
What's wrong with:

# nb: class is a reserved word in Python
ul = HtmlElement('ul', css_class='default')
for i in range(3):
ul.append(HtmlElement('li', 'baaz%d' % i)

# nb2: use the __str__() method of HtmlElement
print str(ul)

Python's philosophy is to make simple things simple (don't worry,
there's still space for complex things -> descriptors, metaclasses etc).

HTH

Merci bien pour votre reponse.

Gerard
 
G

Gerard Flanagan

Fredrik said:
just curious, but what's the purpose of this class, and what gave
you the idea to structure your program in this way ?

</F>

In my defense, I can only quote Albert Einstein: "If we knew what it
was we were doing, it would not be called research, would it?" I'm
writing a script to generate a few relatively static webpages and
upload them to a server, but it's as much a learning exercise as
anything else - I have no formal programming education. I thought the
Factory pattern would be appropriate here; the class you quote was a
first attempt and i knew it was undoubtedly the wrong approach, so
that's why i asked the group...

Gerard
 
K

Kent Johnson

Gerard said:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/86900

mais il utilise 'apply', qui est...blah

I was trying to implement the factory pattern.
The recipe above uses 'apply' which is deprecated according to the
docs, and I suppose I was curious how to do the same sort of thing
without 'apply'.

Apply has been replaced by 'extended call syntax', that is why it is
deprecated. Instead of
return apply(self._function,_args,_kargs)

write
return self._function(*_args, **_kargs)

Kent
 
B

bruno at modulix

bruno at modulix wrote:
(snip)
Je ne vois pas très bien à quoi sert cette classe (à moins bien sûr
qu'il y ait d'autre code). Pour ce que je vois là, pourquoi ne pas
appeler directement les classes HtmlPage, HtmlElement et HtmlLiteral ?
oops, sorry, forgot to remove this before posting :(
 
B

bruno at modulix

Gerard said:
bruno at modulix wrote:
(snip french)

I was trying to implement the factory pattern.
The recipe above uses 'apply' which is deprecated according to the
docs, and I suppose I was curious how to do the same sort of thing
without 'apply'.

def fun(*args, **kwargs):
pass

def otherfun(*args, **kwargs):
fun(*args, **kwargs)
(snip)


Never been to Java in my life!

bad guess.
I don't know if I understand you, HtmlElement has a 'to_string' method

I don't know this HtmlElement class, nor where it comes from.
'to_string()' (or is it toString() ?) is javaish. The pythonic idiom for
this is implementing the __str__() method and calling str(obj) on the
obj (which will call obj.__str__()). Hence my (bad) guess about you
being a Javaer.
but no static methods:

Not the HtmlElement class, but the HtmlBuilder one. Here again, a class
defining only staticmethods is a pretty javaish idiom - in Python, we
just use good old functions !-) (in a separate module if you want a
separate namespace).
class HtmlElement(list):

def __init__(self, tag, text=None, **attrib):
self.tag = tag
self.text = text
self.attrib = attrib

def write(self, writer):
writer.start(self.tag, self.attrib)
if self.text is not None:
writer.data(self.text)
for node in self:
node.write(writer)
writer.end()

def to_string(self):
out = StringIO()
writer = HtmlWriter(out)
self.write(writer)
ret = out.getvalue()
out.close()
return ret

If it's your own code, you may want to rename this last method __str__().
My code itself is just a learning project ( etant sans emploi a ce
moment, moi-meme...) and it is no doubt a bit 'over-egged' at the
minute, but it is a first attempt and I can always refactor later.

My experience is that it's far easier to start simple and add
flexibility where needed than to simplify useless complexity. KISS,
yagni and all that kind of things...
Merci bien pour votre reponse.
You're welcome.

BTW, there's also a french speaking python newsgroup at fr.comp.lang.py.
 
G

Gerard Flanagan

bruno at modulix wrote:
[...]
I don't know this HtmlElement class, nor where it comes from.
'to_string()' (or is it toString() ?) is javaish. The pythonic idiom for
this is implementing the __str__() method and calling str(obj) on the
obj (which will call obj.__str__()). Hence my (bad) guess about you
being a Javaer.

I've made this change, thanks. ( I had a year with C# so maybe that's
why I'm so idiomatically-challenged ! )
Not the HtmlElement class, but the HtmlBuilder one. Here again, a class
defining only staticmethods is a pretty javaish idiom - in Python, we
just use good old functions !-) (in a separate module if you want a
separate namespace).

But it's personal preference, no? The functions were kind of related
and meaningless outside the module they were declared - but I'll take
it on board, anyway.

[...]
My experience is that it's far easier to start simple and add
flexibility where needed than to simplify useless complexity. KISS,
yagni and all that kind of things...

yagni - I'd never heard that one!

I've ditched the factory class in any case:

http://gflanagan.net/site/python/htmlbuilder/HtmlBuilder.py
(FWIW)
You're welcome.

BTW, there's also a french speaking python newsgroup at fr.comp.lang.py.

I wasn't aware of that, I'll have a lurk!

Thanks again.

Gerard
 
B

bruno at modulix

Gerard said:
bruno at modulix wrote:
[...]
I don't know this HtmlElement class, nor where it comes from.
'to_string()' (or is it toString() ?) is javaish. The pythonic idiom for
this is implementing the __str__() method and calling str(obj) on the
obj (which will call obj.__str__()). Hence my (bad) guess about you
being a Javaer.


I've made this change, thanks. ( I had a year with C# so maybe that's
why I'm so idiomatically-challenged ! )

Well, C# being somewhat inspired by Java...
But it's personal preference, no?

Well, in Python, there are a lot of things that can be done the way you
like it, but are better done the idiomatic way. Python relies quite a
lot on conventions and idioms where other languages try to inforce
strict rules.
The functions were kind of related
and meaningless outside the module they were declared -

FWIW (and from the snippet I saw), these functions are useless even in
the module !-)

Unless you want to dynamically choose the concrete class at runtime
based on platform/settings/phase of the moon/whatnot (which seems not to
be te case in the snippet you posted), you don't need these functions,
just instanciating the concrete class is enough. Remember that Python
classes *are* factory already - and that you can freely replace a class
by any callable returning an instance, ie:

== before refactoring, directly instanciating concrete class ==
# myhtmlmodule.py
class HtmlElement(tag, *args, **kw):
# code here

# myclientmodule.py
from myhtmlmodule import HtmlElement
ul = HtmlElement('ul')


== after refactoring, using a factory function ==
# myhtmlmodule.py
class _HtmlElement1(tag, *args, **kw):
# code here

class _HtmlElement2(tag, *args, **kw):
# other code here

# yes, it's now a function...
def HtmlElement(tag, *args, **kw):
if phase_of_the_moon():
klass = _HtmlElement1
else:
klass = _HtmlElement2
return klass(tag, *args, **kw)

# myclientmodule.py
# well... nothing changed here !-)
from myhtmlmodule import HtmlElement
ul = HtmlElement('ul')


You could also do the trick with metaclass black magic, but what, KISS...
but I'll take
it on board, anyway.

[...]

My experience is that it's far easier to start simple and add
flexibility where needed than to simplify useless complexity. KISS,
yagni and all that kind of things...

yagni - I'd never heard that one!

You Aint Gonna Need It.

Code not written is the best code, so don't write code "just in case".
Python is usually dynamic enough to make refactoring easy (cf example above)
I've ditched the factory class in any case:

http://gflanagan.net/site/python/htmlbuilder/HtmlBuilder.py
(FWIW)

Seems mostly clean. May I suggest a couple small corrections/improvements ?

1/ potential bugfix:
try:
from tidy import parseString
except ImportError:
def parseString(text):
# woops, this function is supposed to return something
#pass
return text

2/ safer and cleaner
class HtmlPage(HtmlElement):
# removed class vars,
# replaced with default in __init__
def __init__(self, title, **kw):
self.title = title
self.stylesheets = kw.get('stylesheets', [])
self.doctype = kw.get('doctype', HTML4_STRICT)

I wasn't aware of that, I'll have a lurk!

see you there !-)
 
G

Gerard Flanagan

bruno said:
FWIW (and from the snippet I saw), these functions are useless even in
the module !-)

ok, ok... :)
Unless you want to dynamically choose the concrete class at runtime
based on platform/settings/phase of the moon/whatnot (which seems not to
be te case in the snippet you posted), you don't need these functions,
just instanciating the concrete class is enough. Remember that Python
classes *are* factory already - and that you can freely replace a class
by any callable returning an instance, ie:

== before refactoring, directly instanciating concrete class ==
# myhtmlmodule.py
class HtmlElement(tag, *args, **kw):
# code here

# myclientmodule.py
from myhtmlmodule import HtmlElement
ul = HtmlElement('ul')


== after refactoring, using a factory function ==
# myhtmlmodule.py
class _HtmlElement1(tag, *args, **kw):
# code here

class _HtmlElement2(tag, *args, **kw):
# other code here

# yes, it's now a function...
def HtmlElement(tag, *args, **kw):
if phase_of_the_moon():
klass = _HtmlElement1
else:
klass = _HtmlElement2
return klass(tag, *args, **kw)

# myclientmodule.py
# well... nothing changed here !-)
from myhtmlmodule import HtmlElement
ul = HtmlElement('ul')

ah, I'm getting it.
I've ditched the factory class in any case:

http://gflanagan.net/site/python/htmlbuilder/HtmlBuilder.py
(FWIW)

Seems mostly clean. May I suggest a couple small corrections/improvements ?

1/ potential bugfix:
try:
from tidy import parseString
except ImportError:
def parseString(text):
# woops, this function is supposed to return something
#pass
return text

2/ safer and cleaner
class HtmlPage(HtmlElement):
# removed class vars,
# replaced with default in __init__
def __init__(self, title, **kw):
self.title = title
self.stylesheets = kw.get('stylesheets', [])
self.doctype = kw.get('doctype', HTML4_STRICT)

That's much better - thanks very much for taking the time, I'm a little
bit wiser!

regards

Gerard
 

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,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top