My fight with classes :)

T

TheSaint

Hi,
I'm very new with classes. I still reading something around ;)

I got started to try a concatenation of 2 type of string, which have a
particular property to start with A or D.

My class here:
""" Small class to join some strings according to the leading first
letter"""

def __init__(self):
self.valueA= ''
self.valueD= ''

def __add__(self, value):
if not isinstance(value, str): return
if value.lower().startswith('a'):
self.valueA += value
if value.lower().startswith('d'):
self.valueD += value
return self.valueA ,self.valueD

__call__= __add__
__iadd__= __add__

my test on the shell:
Traceback (most recent call last):
Traceback (most recent call last):
('aksaboi', 'daksdhksduboi')

Do I miss something?

I'd rather like to avoid class, but a function won't allow me to store so
easily data between several call.
Mostly I'd expect to pass to the built instance in a more elaborated
function. Then I mean call will be the primer goal.
 
T

Terry Reedy

| Hi,
| I'm very new with classes. I still reading something around ;)
|
| I got started to try a concatenation of 2 type of string, which have a
| particular property to start with A or D.
|
| My class here:
| """ Small class to join some strings according to the leading first
| letter"""

You left out the class statement.

| def __init__(self):
| self.valueA= ''
| self.valueD= ''
|
| def __add__(self, value):

I agree with P. Pearson that 'add' methods should generaly not be used for
mutation. Certainly, they are generally used to combine two objects of the
same or compatible classes, even if the result replaces one of them. This
method is equivalent to list.append.

| if not isinstance(value, str): return

Do you really want to just return None when there is bad input?

| if value.lower().startswith('a'):
| self.valueA += value
| if value.lower().startswith('d'):
| self.valueD += value
| return self.valueA ,self.valueD

List mutation methods return None so one cannot forget that they mutate.
In any case, the alternative is to return self. You seem to be returning
this tuple because you did not write an __str__ method. Doing two
different things in one method is what got you in trouble. So return None
or self and add

def __str__(self): return self.valueA + ', ' + self.valueB

| __call__= __add__
| __iadd__= __add__

This requires that __add__ return self. Better to use .append() and

def __iadd__(self, val):
self.append(val)
return self

| my test on the shell:
[snip good tests]
| >>> k += 'liu'

k is now a tuple!
Hence the below

| >>> k += 'aliu'
| Traceback (most recent call last):
| File "<stdin>", line 1, in <module>
| TypeError: can only concatenate tuple (not "str") to tuple
| >>> k
| ('aksaboi', 'daksdhksduboi')

| Do I miss something?

That augmented assignment is assigment. Always.
You are not the first ;-)/

| I'd rather like to avoid class, but a function won't allow me to store so
| easily data between several call.

Classes are the general mechanism for bundling stored data and functions.
You can easily bundle *one* function and stored data with a nested
function.

def makeappender():
data = ['','']
def appender(val):
<code that mutates data>
return appender

For multiple functions, use classes.

| Mostly I'd expect to pass to the built instance in a more elaborated
| function. Then I mean call will be the primer goal.

__call__ can be an alias for append just as well as for __add__.

Terry Jan Reedy
 
T

TheSaint

On 04:51, giovedì 12 giugno 2008 Terry Reedy wrote:

First of all a big thank you, all.
def makeappender():
data = ['','']
def appender(val):
<code that mutates data>
return appender

I'll give it a try. I just doubting if the data will be shared outside the
function.
Actually, my practice goes to send all variables to the functions and
expecting a returned value. Usually I'm not relying on that module's
variables are read inside a function. Probably I got wrong learning or
experiences.
For multiple functions, use classes.

That's what I'm leaning to :)
Then I re-elaborated the class according your points and now it's what I
wanted to be. :)
(last time I forgot to past the first line)
Here it comes:

class StrJoin:
""" Join a pair of strings according to the leading first letter A or D,
it returns a list of 2 elements"""

def __init__(self):
self.valueA= ''
self.valueD= ''

def append(self, value):
if not isinstance(value, str):
raise TypeError, 'Wrong type concatenation'
if value.lower().startswith('a'):
self.valueA += value
if value.lower().startswith('d'):
self.valueD += value
return [self.valueA ,self.valueD]

def __getitem__(self,idx):
if idx > 1 : return self
if idx == 0 : return self.valueA
if idx == 1 : return self.valueD

__call__= append

def __repr__(self):
return '['+ self.valueA+ ','+ self.valueD+ ']'

And the shell >>:
from utilities import StrJoin as zx
k = zx()
k [,]
k('add') ['add', '']
k[2] [add,]
k[1] ''
k[0] 'add'
k('dad') ['add', 'dad']
k('sad') ['add', 'dad']
k('Alfa') ['addAlfa', 'dad']
k('Dude') ['addAlfa', 'dadDude']
k('Omega') ['addAlfa', 'dadDude']
k('Dome') ['addAlfa', 'dadDudeDome']
k.append[k]
Traceback (most recent call last):
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "utilities.py", line 33, in append
raise TypeError, 'Wrong type concatenation'
TypeError: Wrong type concatenation
Mostly I'll use the call option. I also like to pass it into a function in
order to modularize the loop where it gets started.
 
B

Bruno Desthuilliers

TheSaint a écrit :
On 04:51, giovedì 12 giugno 2008 Terry Reedy wrote:

First of all a big thank you, all.
def makeappender():
data = ['','']
def appender(val):
<code that mutates data>
return appender

I'll give it a try. I just doubting if the data will be shared outside the
function.

Each time makeappender is called, it returns a new appender function
object with it's own 'data' object. So 'data' won't be shared between
different instances of the appender function.
Actually, my practice goes to send all variables to the functions and
expecting a returned value.

Mostly a sane, sound and sensible approach IMHO - but doesn't work that
well when the function needs to maintain own state between calls.
Usually I'm not relying on that module's
variables are read inside a function. Probably I got wrong learning or
experiences.

As long as you only *read* module's globals from within a function,
that's mostly ok. When you start *writing* them it may be time to
reconsider the design (not that it's necessarily bad, but it's a
possible signal that something is wrong).

Well... Closures are poor men's objects, or so they say (or is that the
other way round ?-).

def make_person(name, age):
state = dict(name=name, age=age)
def set_name(new_name=None):
state['name'] = new_name
def get_name():
return state['name']
def grow():
state['age'] += 1
def get_age()
return state['age']
return set_name, get_name, grow, get_age

(toto_set_name,
toto_get_name,
toto_grow,
toto_get_age) = make_person('toto', 42)

A bit cumbersome, indeed !-)
 
T

TheSaint

Well... Closures are poor men's objects, or so they say (or is that the
other way round ?-).

Well, I'd like to know what could be the reason to design a single-call class
instead of a similar function.
def make_person(name, age):
state = dict(name=name, age=age)
def set_name(new_name=None):

I'm going to get a deeper thinking about a function's function :)

--
Mailsweeper Home : http://it.geocities.com/call_me_not_now/index.html
 
B

Bruno Desthuilliers

TheSaint a écrit :
Well, I'd like to know what could be the reason to design a single-call class
instead of a similar function.

Convenience. FWIW, in Python, functions are objects, and when you use a
closure to maintain state, you in fact already use the function's class
features. Sometimes, it's just simpler and more straightforward to use a
custom callable object than closures. Two common examples are function
decorators taking arguments (which require "two levels" closures if you
want to define them as functions, something that the instanciation/call
scheme of a callable class handles naturally) and partial application.
 

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,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top