473,583 Members | 3,010 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Proposal: reducing self.x=x; self.y=y; self.z=z boilerplate code

*************** *************** *************** *************** *************** ***
This posting is also available in HTML format:
http://cci.lbl.gov/~rwgk/python/adop...005_07_02.html
*************** *************** *************** *************** *************** ***

Hi fellow Python coders,

I often find myself writing::

class grouping:

def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
# real code, finally

This becomes a serious nuisance in complex applications with long
argument lists, especially if long variable names are essential for
managing the complexity. Therefore I propose that Python includes
built-in support for reducing the ``self.x=x`` clutter. Below are
arguments for the following approach (*please* don't get too agitated
about the syntax right here, it really is a secondary consideration): :

class grouping:

def __init__(self, .x, .y, .z):
# real code right here

Emulation using existing syntax::

def __init__(self, x, y, z):
self.x = x
del x
self.y = y
del y
self.z = z
del z
Is it really that important?
----------------------------

For applications of non-trivial size, yes. Here is a real-world example
(one of many in that source tree):
http://cvs.sourceforge.net/viewcvs.p...py?view=markup

Fragment from this file::

class manager:

def __init__(self,
crystal_symmetr y=None,
model_indices=N one,
conformer_indic es=None,
site_symmetry_t able=None,
bond_params_tab le=None,
shell_sym_table s=None,
nonbonded_param s=None,
nonbonded_types =None,
nonbonded_funct ion=None,
nonbonded_dista nce_cutoff=None ,
nonbonded_buffe r=1,
angle_proxies=N one,
dihedral_proxie s=None,
chirality_proxi es=None,
planarity_proxi es=None,
plain_pairs_rad ius=None):
self.crystal_sy mmetry = crystal_symmetr y
self.model_indi ces = model_indices
self.conformer_ indices = conformer_indic es
self.site_symme try_table = site_symmetry_t able
self.bond_param s_table = bond_params_tab le
self.shell_sym_ tables = shell_sym_table s
self.nonbonded_ params = nonbonded_param s
self.nonbonded_ types = nonbonded_types
self.nonbonded_ function = nonbonded_funct ion
self.nonbonded_ distance_cutoff = nonbonded_dista nce_cutoff
self.nonbonded_ buffer = nonbonded_buffe r
self.angle_prox ies = angle_proxies
self.dihedral_p roxies = dihedral_proxie s
self.chirality_ proxies = chirality_proxi es
self.planarity_ proxies = planarity_proxi es
self.plain_pair s_radius = plain_pairs_rad ius
# real code, finally

Not exactly what you want to see in a high-level language.
Is there a way out with Python as-is?
-------------------------------------

Yes. If you take the time to look at the file in the CVS you'll find
that I was cheating a bit. To reduce the terrible clutter above, I am
actually using a simple trick::

adopt_init_args (self, locals())

For completeness, the implementation of ``adopt_init_ar gs()`` is here:
http://cvs.sourceforge.net/viewcvs.p...py?view=markup

While this obviously goes a long way, it has several disadvantages:

- The solution doesn't come with Python -> everybody has to reinvent.

- People are reluctant to use the trick since scripts become
dependent on a non-standard feature.

- It is difficult to remember which ``import`` to use for
``adopt_init_ar gs`` (since everybody has a local version/variety).

- The ``adopt_init_ar gs(self, locals())`` incantation is hard to
remember and difficult to explain to new-comers.

- Inside the ``__init__()`` method, the same object has two names,
e.g. ``x`` and ``self.x``. This lead to subtle bugs a few times
when I accidentally assigned to ``x`` instead of ``self.x`` or vice
versa in the wrong place (the bugs are typically introduced while
refactoring).

- In some cases the ``adopt_init_ar gs()`` overhead was found to
introduce a significant performance penalty (in particular the
enhanced version discussed below).

- Remember where Python comes from: it goes back to a teaching
language, enabling mere mortals to embrace programming.
``adopt_init_ar gs(self, locals())`` definitely doesn't live up
to this heritage.
Minimal proposal
----------------

My minimal proposal is to add an enhanced version of ``adopt_init_ar gs()``
as a standard Python built-in function (actual name secondary!)::

class grouping:

def __init__(self, x, y, z):
adopt_init_args ()
# real code

Here is a reference implementation:
http://cvs.sourceforge.net/viewcvs.p....2&view=markup

Implementation of this proposal would remove all the disadvantages
listed above. However, there is another problem not mentioned before:
It is cumbersome to disable adoption of selected variables. E.g.::

class grouping:

def __init__(self, keep_this, and_this, but_not_this, but_this_again) :
self.keep_this = keep_this
self.and_this = and_this
self.but_this_a gain = but_this_again
# real code, finally

would translate into::

class grouping:

def __init__(self, keep_this, and_this, but_not_this, but_this_again) :
adopt_init_args (exclusions=["but_not_th is"])
# real code
Enhanced syntax proposal
------------------------

The exclusion problem suggests these alternatives::

class grouping:

def __init__(self, self.keep_this, self.and_this, but_not_this,
self.but_this_a gain):
# real code right here

This is conceptually similar to the existing automatic unpacking of tuples.

A shorter alternative (my personal favorite since minimally redundant)::

class grouping:

def __init__(self, .keep_this, .and_this, but_not_this, .but_this_again ):
# real code right here

I guess both versions could be implemented such that users don't incur
a performance penalty compared to the ``self.x=x`` alternative. At the
danger of being overly optimistic: I can imagine that my favorite
alternative will actually be faster (and the fastest).
Enhanced __slot__ semantics proposal
------------------------------------

When ``__slots__`` are used (cool feature!) the boilerplate problem
becomes even worse::

class grouping:

__slots__ = ["keep_this" , "and_this", "but_this_again "]

def __init__(self, keep_this, and_this, but_not_this, but_this_again) :
self.keep_this = keep_this
self.and_this = and_this
self.but_this_a gain = but_this_again
# real code, finally

Each variable name appears four times! Imagine yourself having to
do this exercise given the real-world example above. Ouch.

Based on the "Enhanced syntax proposal" above I see this potential
improvement::

class grouping:

__slots__ = True

def __init__(self, .keep_this, .and_this, but_not_this, .but_this_again ):
# real code right here

Each variable name appears only once. Phew!

Author: rw**@yahoo.com, July 02, 2005

P.S.: If you reply to this message, please clearly separate
naming/syntax issues from the core issue of providing built-in support
designed to reduce clutter.


_______________ _______________ _______________ _______
Yahoo! Sports
Rekindle the Rivalries. Sign up for Fantasy Football
http://football.fantasysports.yahoo.com
Jul 19 '05 #1
15 2579
"Ralf W. Grosse-Kunstleve" <rw**@yahoo.com > wrote:
class grouping:

def __init__(self, .keep_this, .and_this, but_not_this, .but_this_again ):
# real code right here


I'm really torn about this. On the one hand, my first thought was "you
shouldn't be writing constructors with arguments lists so long that this is
a problem", but you showed a reasonable counter-example to that argument.
I'm a big fan of DRY (Don't Repeat Yourself), sometimes expressed as "Once
And Only Once", and your proposal lets you do that.

It also has the nice feature that it doesn't break any existing code; the
suggested syntax is not currently legal Python.

What happens if I do:

def __init__ (self, .x, .y, .z):
x = 0

what does the assignment x do? Does it automatically get promoted to an
assignment to self.x? Does it generate an error?

The big question in my mind is not "Is this useful" (since it clearly is),
but "Does the utility justify the cost?". In other words, will it be used
frequently enough to compensate for the added complexity to the language?
I'm not convinced of that.

There have been some proposals floating around to implement a "with"
keyword which would create implicit namespaces. That's sort of what you're
proposing here. I'm not convinced either is a good idea, but if they were
to be adopted, I'd certainly want to see the them done in a uniform,
logically consistent way.
Jul 19 '05 #2
"Ralf W. Grosse-Kunstleve" <rw**@yahoo.com > wrote in message
news:ma******** *************** *************** *@python.org...
class grouping:

def __init__(self, .x, .y, .z):
# real code right here Emulation using existing syntax:: def __init__(self, x, y, z):
self.x = x
del x
self.y = y
del y
self.z = z
del z


I think this is a bad idea, for a subtle reason.

In Python, unlike many other languages, the names of formal parameters are
part of a function's interface. For example:

def f(x, y):
return x-y

Now f(3, 4) is -1 and f(y=3,x=4) is 1.

The names of instance variables are generally not part of a class'
interface--they are part of its implementation.

This proposed feature, whenever used, would tie a class' implementation to
the interface of every method that uses the feature. As far as I can see,
it is impossible to use the feature without constraining the implementation
in this way.

For this reason, I would much rather have the mapping between parameter
names and instance variables be explicit.
Jul 19 '05 #3
"Andrew Koenig" <ar*@acm.org> wrote:
In Python, unlike many other languages, the names of formal parameters are
part of a function's interface. For example:

def f(x, y):
return x-y

Now f(3, 4) is -1 and f(y=3,x=4) is 1.

The names of instance variables are generally not part of a class'
interface--they are part of its implementation.

This proposed feature, whenever used, would tie a class' implementation to
the interface of every method that uses the feature. As far as I can see,
it is impossible to use the feature without constraining the implementation
in this way.


While I suppose that's true from a theoretical point of view, as a
practical matter, I don't see it being much of a big deal. I don't think
I've ever written an __init__ method which saved its parameters and used
different names for the parameter and the corresponding instance variable.
Doing so would just be confusing (at least for the kind of code I write).

Also, it doesn't really tie it in any hard and fast way. Right now, I
would write:

def __init__ (self, x, y, z):
self.x = x
self.y = y
self.z = z
blah

under the new proposal, I would write:

def __init__ (self, .x, .y, .z):
blah

If at some time in the future, if I decided I need to change the name of
the instance variable without changing the exposed interface, it would be
easy enough to do:

def __init__ (self, .x, .y, z):
self.zeta = z
blah

I'm still not convinced we need this, but the exposed interface issue
doesn't worry me much.
Jul 19 '05 #4
Why not just update the local dictionary?

class Grouping:
def __init__(self,x ,y,z):
self.__dict__.u pdate(locals())
Jul 19 '05 #5
Ralf W. Grosse-Kunstleve wrote:
class grouping:

def __init__(self, .x, .y, .z):
# real code right here
The way this would work seems a bit inconsistent to me. Args normally
create local variables that receive references to the objects passed to
them.

In this case, a function/method is *creating* non local names in a scope
outside it's own name space to receive it's arguments. I don't think
that's a good idea.

A better approach is to have a copy_var_to(des t, *var_list) function
that can do it. You should be able to copy only selected arguments, and
not all of them.

copy_var_to(sel f,x,z)

Not exactly straight forward to do as it runs into the getting an
objects name problem.

Emulation using existing syntax::

def __init__(self, x, y, z):
self.x = x
del x
self.y = y
del y
self.z = z
del z
The 'del's aren't needed as the references will be unbound as soon as
__init__ is finished. That's one of the reasons you need to do self.x=x
, the other is to share the objects with other methods.
Is there a way out with Python as-is?
-------------------------------------


With argument lists that long it might be better to use a single
dictionary to keep them in.

class manager:
def __init__(self, **args):
defaults = {
'crystal_symmet ry':None,
'model_indices' :None,
'conformer_indi ces':None,
'site_symmetry_ table':None,
'bond_params_ta ble':None,
'shell_sym_tabl es':None,
'nonbonded_para ms':None,
'nonbonded_type s':None,
'nonbonded_func tion':None,
'nonbonded_dist ance_cutoff':No ne,
'nonbonded_buff er':1,
'angle_proxies' :None,
'dihedral_proxi es':None,
'chirality_prox ies':None,
'planarity_prox ies':None,
'plain_pairs_ra dius':None }
defaults.update (args)
self.data = defaults

# real code

Regards,
Ron
Jul 19 '05 #6
In article <Tm************ *********@news2 0.bellglobal.co m>,
"Walter Brunswick" <wa************ *@sympatico.ca> wrote:
Why not just update the local dictionary?

class Grouping:
def __init__(self,x ,y,z):
self.__dict__.u pdate(locals())


That's pretty clever. The only minor annoyance is that it creates a
self.self. If that bothers you, you can fix it with:

def __init__ (self, x, y, z):
vars = locals()
del vars["self"]
self.__dict__.u pdate(vars)

or, perhaps:

def __init__ (self, x, y, z):
self.__dict__.u pdate(locals())
del self.self

It doesn't give you all the flexibility of the original proposal (i.e.
name-by-name selectivity of what gets imported into self), but it does
solve the OP's OP (Original Poster's Original Problem).
Jul 19 '05 #7
Ralf W. Grosse-Kunstleve wrote:
A shorter alternative (my personal favorite since minimally redundant)::

class grouping:

def __init__(self, .keep_this, .and_this, but_not_this, .but_this_again ):
# real code right here
There is also the variant which I proposed on python-dev:

class grouping:
def __init__(self, _keep_this, _and_this, but_not_this, _but this
again):
InitAttrs(self, locals())
#real code goes here

Essentially you replace the '.' with '_', which doesn't require a
syntax change. Unfortunately, both are almost invisible. It does
offer you what you want right now (without that whole waiting a year+
for Python 2.5, PEP process, etc.).
Enhanced __slot__ semantics proposal
------------------------------------

When ``__slots__`` are used (cool feature!) the boilerplate problem
becomes even worse::

class grouping:

__slots__ = ["keep_this" , "and_this", "but_this_again "]

def __init__(self, keep_this, and_this, but_not_this, but_this_again) :
self.keep_this = keep_this
self.and_this = and_this
self.but_this_a gain = but_this_again
# real code, finally
There is also the AutoSlots metaclass (which I have fixed) that does
this as well.

class grouping(object ):
__metaclass__ = AutoSlots
def __init__(self, _keep_this, _and_this, but_not_this,
_but_this_again ):
InitAttrs(self, locals())
#real code goes here

Both AutoSlots and InitAttrs use leading underscores on the __init__
method to discover which attributes are to be __slots__, and which
should be automatically assigned.
P.S.: If you reply to this message, please clearly separate
naming/syntax issues from the core issue of providing built-in support
designed to reduce clutter.


Because you don't seem to have listened in python-dev, I'll say it
here. Not everything that reduces clutter should be syntax, and not
every function, class, and module which reduces programming time should
be builtin. Why?
1. Expanding Python syntax bloats the language. It increases what you
need to teach to new Python users. In my opinion, syntax additions
should really only be considered when significant gains in readability
and writability for many users are realized, that a lack of syntax
cannot offer.

2. Expanding the Python standard library bloats the distribution.
Right now, Python is a relatively light download. But if one were to
include the top packages in everything, the distribution would quickly
bloat to 40+ megs. This is not an option. Generally, the requirements
of getting code into the standard library is either a demonstrated need
for the addition, or a known best-of-breed implementation for a
commonly used module, or both.

I believe your syntax change is a non-starter. Why? Because I've
offered code that does essentially everything you want, without a
syntax change. If someone happens to include AutoSlots and InitAttrs
with their code, module, what have you, and it manages to make its way
into standard Python, so be it (I place the code into the public
domain).

The code for the InitAttrs and AutoSlots mechanism are available here:
http://aspn.activestate.com/ASPN/Coo.../Recipe/435880

- Josiah

Jul 19 '05 #8
On Sat, 02 Jul 2005 13:50:25 GMT, "Andrew Koenig" <ar*@acm.org> wrote:
"Ralf W. Grosse-Kunstleve" <rw**@yahoo.com > wrote in message
news:ma******* *************** *************** **@python.org.. .
class grouping:

def __init__(self, .x, .y, .z):
# real code right here

Emulation using existing syntax::

def __init__(self, x, y, z):
self.x = x
del x
self.y = y
del y
self.z = z
del z


I think this is a bad idea, for a subtle reason.

In Python, unlike many other languages, the names of formal parameters are
part of a function's interface. For example:

def f(x, y):
return x-y

Now f(3, 4) is -1 and f(y=3,x=4) is 1.

The names of instance variables are generally not part of a class'
interface--they are part of its implementation.

This proposed feature, whenever used, would tie a class' implementation to
the interface of every method that uses the feature. As far as I can see,
it is impossible to use the feature without constraining the implementation
in this way.

For this reason, I would much rather have the mapping between parameter
names and instance variables be explicit.

What if parameter name syntax were expanded to allow dotted names as binding
targets in the local scope for the argument or default values? E.g.,

def foometh(self, self.x=0, self.y=0): pass

would have the same effect as

def foometh(self, self.y=0, self.x=0): pass

and there would be a persistent effect in the attributes of self
(whatever object that was), even with a body of pass.

I'm not sure about foo(self, **{'self.x':0, 'self.y':0}), but if
you didn't capture the dict with a **kw formal parameter, IWT you'd
have to be consistent and effect the attribute bindings implied.

(Just a non-thought-out bf here, not too serious ;-)

Regards,
Bengt Richter
Jul 21 '05 #9
Bengt Richter wrote:
What if parameter name syntax were expanded to allow dotted names as binding
targets in the local scope for the argument or default values? E.g.,

def foometh(self, self.x=0, self.y=0): pass

would have the same effect as

def foometh(self, self.y=0, self.x=0): pass

and there would be a persistent effect in the attributes of self
(whatever object that was), even with a body of pass.

I'm not sure about foo(self, **{'self.x':0, 'self.y':0}), but if
you didn't capture the dict with a **kw formal parameter, IWT you'd
have to be consistent and effect the attribute bindings implied.

(Just a non-thought-out bf here, not too serious ;-)

Regards,
Bengt Richter


Well it works the other way around to some degree.

def foo(self, x=x, y=y):pass

x=x binds the class variables to the arguments without the self. if no
value is given.

Which is kind of strange, since x by it self gives an error if no value
is given. The strange part is x=x is not the same as just x. I
understand why, but it still looks odd.
Why isn't there a dict method to get a sub dict from a key list?
Fromkeys doesn't quite do it.

sub-dict = dict.subdict(ke y_list)

Or have dict.copy() take a key list. (?)

<Just a thought.>

The following works and doesn't seem too obscure, although the x=x,
etc.. could be annoying if they were a lot of long names.

Seems like mutable default arguments is what's needed to make it work,
not that it's needed IMO. But it's an interesting problem.
def subdict(dict, keys):
d = {}
for k in keys:
d[k] = dict[k]
return d

class foo(object):
x = 1
y = 2
z = 3
def __init__(self,x =x,y=y,z=z):
save_these = subdict(locals( ),['x','y'])
self.__dict__.u pdate(save_thes e)

# rest of code
print self.x, self.y, self.z

f = foo()
f = foo(5,6,7)


Jul 21 '05 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

9
2540
by: Jess Austin | last post by:
hi, I like the way that Python does lists, and I love the way it does iterators. But I've decided I don't like what it does with iterators of lists. Lists are supposed to be mutable sequences, but try to use an iterator of a list that you're mutating and watch it all fall to pieces. That is, if you change the length of a section of the...
31
2410
by: Brian Sabbey | last post by:
Here is a pre-PEP for what I call "suite-based keyword arguments". The mechanism described here is intended to act as a complement to thunks. Please let me know what you think. Suite-Based Keyword Arguments ----------------------------- Passing complicated arguments to functions is currently awkward in Python. For example, the typical...
18
2251
by: Ralf W. Grosse-Kunstleve | last post by:
My initial proposal (http://cci.lbl.gov/~rwgk/python/adopt_init_args_2005_07_02.html) didn't exactly get a warm welcome... And Now for Something Completely Different: class autoinit(object): def __init__(self, *args, **keyword_args): self.__dict__.update(
47
3331
by: Pierre Barbier de Reuille | last post by:
Please, note that I am entirely open for every points on this proposal (which I do not dare yet to call PEP). Abstract ======== This proposal suggests to add symbols into Python. Symbols are objects whose representation within the code is more important than their actual value. Two symbols needs only to be
9
2021
by: corey.coughlin | last post by:
Alright, so I've been following some of the arguments about enhancing parallelism in python, and I've kind of been struck by how hard things still are. It seems like what we really need is a more pythonic approach. One thing I've been seeing suggested a lot lately is that running jobs in separate processes, to make it easy to use the latest...
26
2697
by: brenocon | last post by:
Hi all -- Compared to the Python I know and love, Ruby isn't quite the same. However, it has at least one terrific feature: "blocks". Whereas in Python a "block" is just several lines of locally-scoped-together code, in Ruby a "block" defines a closure (anonymous function). To avoid confusion let's call them Ruby block-closures. I see...
0
7895
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main...
0
7826
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language...
0
8182
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. ...
0
8327
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that...
0
8193
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the...
0
5374
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert...
1
2333
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
1
1433
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
0
1157
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.