Mailing List Archive

Configuring an object via a dictionary
Hi,

I am initialising an object via the following:

def __init__(self, config):

self.connection = None

self.source_name = config['source_name']
self.server_host = config['server_host']
self.server_port = config['server_port']
self.user_base = config['user_base']
self.user_identifier = config['user_identifier']
self.group_base = config['group_base']
self.group_identifier = config['group_identifier']
self.owner_base = config['owner_base']

However, some entries in the configuration might be missing. What is
the best way of dealing with this?

I could of course simply test each element of the dictionary before
trying to use. I could also just write

self.config = config

but then addressing the elements will add more clutter to the code.

However, with a view to asking forgiveness rather than
permission, is there some simple way just to assign the dictionary
elements which do in fact exist to self-variables?

Or should I be doing this completely differently?

Cheers,

Loris

--
This signature is currently under constuction.
--
https://mail.python.org/mailman/listinfo/python-list
Re: Configuring an object via a dictionary [ In reply to ]
On 3/15/24 03:30, Loris Bennett via Python-list wrote:
> Hi,
>
> I am initialising an object via the following:

> self.source_name = config['source_name']

config.get('source_name', default_if_not_defined) is a common technique...




--
https://mail.python.org/mailman/listinfo/python-list
Re: Configuring an object via a dictionary [ In reply to ]
On 3/15/2024 5:30 AM, Loris Bennett via Python-list wrote:
> Hi,
>
> I am initialising an object via the following:
>
> def __init__(self, config):
>
> self.connection = None
>
> self.source_name = config['source_name']
> self.server_host = config['server_host']
> self.server_port = config['server_port']
> self.user_base = config['user_base']
> self.user_identifier = config['user_identifier']
> self.group_base = config['group_base']
> self.group_identifier = config['group_identifier']
> self.owner_base = config['owner_base']
>
> However, some entries in the configuration might be missing. What is
> the best way of dealing with this?
>
> I could of course simply test each element of the dictionary before
> trying to use. I could also just write
>
> self.config = config
>
> but then addressing the elements will add more clutter to the code.
>
> However, with a view to asking forgiveness rather than
> permission, is there some simple way just to assign the dictionary
> elements which do in fact exist to self-variables?
>
> Or should I be doing this completely differently?

self.source_name = config.get('source_name', default_value)

Or, if you like this kind of expression better,

self.source_name = config.get('source_name') or default_value

.get() will return None if the key doesn't exist, or the default value
if you specify one.
--
https://mail.python.org/mailman/listinfo/python-list
Re: Configuring an object via a dictionary [ In reply to ]
On 3/15/24 02:30, Loris Bennett wrote:
> Hi,
>
> I am initialising an object via the following:
>
> def __init__(self, config):
>
> self.connection = None
>
> self.source_name = config['source_name']
> self.server_host = config['server_host']

> However, with a view to asking forgiveness rather than
> permission, is there some simple way just to assign the dictionary
> elements which do in fact exist to self-variables?

class Foo():

def __init__(self, config):

for key, val in config.iteritems():
setattr(self, key, val)

f = Foo({'cat': 'dog'})

print(f.cat)

(outputs 'dog')

--
https://mail.python.org/mailman/listinfo/python-list
Re: Configuring an object via a dictionary [ In reply to ]
On 2024-03-15, Thomas Passin via Python-list <python-list@python.org> wrote:
> On 3/15/2024 5:30 AM, Loris Bennett via Python-list wrote:
>> Hi,
>>
>> I am initialising an object via the following:
>>
>> def __init__(self, config):
>>
>> self.connection = None
>>
>> self.source_name = config['source_name']
>> self.server_host = config['server_host']
>> self.server_port = config['server_port']
>> self.user_base = config['user_base']
>> self.user_identifier = config['user_identifier']
>> self.group_base = config['group_base']
>> self.group_identifier = config['group_identifier']
>> self.owner_base = config['owner_base']
>>
>> However, some entries in the configuration might be missing. What is
>> the best way of dealing with this?
>>
>> I could of course simply test each element of the dictionary before
>> trying to use. I could also just write
>>
>> self.config = config
>>
>> but then addressing the elements will add more clutter to the code.
>>
>> However, with a view to asking forgiveness rather than
>> permission, is there some simple way just to assign the dictionary
>> elements which do in fact exist to self-variables?
>>
>> Or should I be doing this completely differently?
>
> self.source_name = config.get('source_name', default_value)
>
> Or, if you like this kind of expression better,
>
> self.source_name = config.get('source_name') or default_value

Won't the latter version misbehave if the value of config['source_name'] has a
"false" boolean value (e.g. "", 0, 0.0, None, [], (), {}, ...)

>>> config = {}
>>> config['source_name'] = ""
>>> config.get('source_name') or 'default'
'default'



--
https://mail.python.org/mailman/listinfo/python-list
Re: Configuring an object via a dictionary [ In reply to ]
I should mention that I wanted to answer your question,
but I wouldn't actually do this. I'd rather opt for
your self.config = config solution. The config options
should have their own namespace.

I don't mind at all referencing foo.config['option'],
or you could make foo.config an object by itself so
you can do foo.config.option. You'd fill it's attributes
in the same way I suggested for your main object.




--
https://mail.python.org/mailman/listinfo/python-list
Re: Configuring an object via a dictionary [ In reply to ]
On 3/15/2024 3:09 PM, Grant Edwards via Python-list wrote:
> On 2024-03-15, Thomas Passin via Python-list <python-list@python.org> wrote:
>> On 3/15/2024 5:30 AM, Loris Bennett via Python-list wrote:
>>> Hi,
>>>
>>> I am initialising an object via the following:
>>>
>>> def __init__(self, config):
>>>
>>> self.connection = None
>>>
>>> self.source_name = config['source_name']
>>> self.server_host = config['server_host']
>>> self.server_port = config['server_port']
>>> self.user_base = config['user_base']
>>> self.user_identifier = config['user_identifier']
>>> self.group_base = config['group_base']
>>> self.group_identifier = config['group_identifier']
>>> self.owner_base = config['owner_base']
>>>
>>> However, some entries in the configuration might be missing. What is
>>> the best way of dealing with this?
>>>
>>> I could of course simply test each element of the dictionary before
>>> trying to use. I could also just write
>>>
>>> self.config = config
>>>
>>> but then addressing the elements will add more clutter to the code.
>>>
>>> However, with a view to asking forgiveness rather than
>>> permission, is there some simple way just to assign the dictionary
>>> elements which do in fact exist to self-variables?
>>>
>>> Or should I be doing this completely differently?
>>
>> self.source_name = config.get('source_name', default_value)
>>
>> Or, if you like this kind of expression better,
>>
>> self.source_name = config.get('source_name') or default_value
>
> Won't the latter version misbehave if the value of config['source_name'] has a
> "false" boolean value (e.g. "", 0, 0.0, None, [], (), {}, ...)
>
>>>> config = {}
>>>> config['source_name'] = ""
>>>> config.get('source_name') or 'default'
> 'default'

Oh, well, picky, picky! I've always like writing using the "or" form
and have never gotten bit - especially for configuration-type values
where you really do expect a non-falsey value, it's probably low risk -
but of course, you're right. In newer code I have been putting a default
into get(). And I suppose there is always the possibility that sometime
in the future an "or" clause like that will be changed to return a
Boolean, which one would expect anyway.

--
https://mail.python.org/mailman/listinfo/python-list
Re: Configuring an object via a dictionary [ In reply to ]
On 2024-03-15 at 15:48:17 -0400,
Thomas Passin via Python-list <python-list@python.org> wrote:

> [...] And I suppose there is always the possibility that sometime in
> the future an "or" clause like that will be changed to return a
> Boolean, which one would expect anyway.

Not only is the current value is way more useful, but changing it would
be a compatibility and maintenance nightmare.

If I want Java, I know where to find it. :-)
--
https://mail.python.org/mailman/listinfo/python-list
Re: Configuring an object via a dictionary [ In reply to ]
On 15/03/24 22:30, Loris Bennett via Python-list wrote:
> Hi,
>
> I am initialising an object via the following:
>
> def __init__(self, config):
>
> self.connection = None
>
> self.source_name = config['source_name']
> self.server_host = config['server_host']
> self.server_port = config['server_port']
> self.user_base = config['user_base']
> self.user_identifier = config['user_identifier']
> self.group_base = config['group_base']
> self.group_identifier = config['group_identifier']
> self.owner_base = config['owner_base']
>
> However, some entries in the configuration might be missing. What is
> the best way of dealing with this?

How do you define "missing"?

Thus, @Thomas' suggestion may/not apply. It is neat and easy.

I usually plump for:

self.source_name = config[ "source_name" ] or default_value

but @Grant's warning applies!
(which is why the pythonic way is to use (and test for) None as a
definition of "missing" (see also impacts of using default values and
mutable data-structures)


LBYL cf EAFP:
When setting-up an environment like this, elements are often set to a
default-value, and then user-settings, command-line arguments, and the
like, applied in a priority-order sequence, amend as-appropriate. In
this way, such problems will never arise.

This is the better course 90% of the time.


Which raises the next question:

What is the impact if some attribute is "missing"? ie if the 'whatever'
must be identified by source_name (for example), then there is little
point in assigning any values to the new class. Considerations which
apply *after* this question, the __init__(), are at least
equally-important considerations (see below)!


> I could of course simply test each element of the dictionary before
> trying to use. I could also just write
>
> self.config = config
>
> but then addressing the elements will add more clutter to the code.

By which you mean that such "clutter" should only appear in the
__init__() - which is not such a bad idea (but see below).

OTOH it is often helpful to one's comprehension to be given a prompt as
to the source of the data. Thus, in later processing *config[
"source_name" ] may add a small amount of extra information to the
reader, over self.source_name.

* maybe prepended "self.", or not...


Am assuming that passing all eight elements as individual arguments is
off-the-table, embodying far too many 'negatives' (see below).


> However, with a view to asking forgiveness rather than
> permission, is there some simple way just to assign the dictionary
> elements which do in fact exist to self-variables?

Assuming config is a dict:

self.__dict__.update( config )

will work, but attracts similar criticism - if not "clutter" then an
unnecessary view (and understanding) of the workings of classes
under-the-hood.


Another question:
When these values are used, are they all used at the same time, and
never again? It may only be worth 'breaking-out' and making attributes
from those which are used in multiple situations within the class's
methods. If, the other extreme, they are only (effectively) passed from
the __init__() to some sort of open() method, then pass the
data-structure as an whole and delegate/remove the "clutter" to there.
In that scenario, such detail would *only* has meaning *and* purpose in
the open() method and thus no point in cluttering-up the __init__() with
detail that is only required elsewhere!


> Or should I be doing this completely differently?

YMMV, but I prefer the idea of transferring the environment/config as a
whole (as above).

If it were a class (cf the supposed dict) then "clutter" is reduced by
eschewing brackets and quotation-marks, eg "config.source_name".


If those eight-elements are not the entirety of that data-structure,
then consider creating an interface-class, which is extracted (or built
that way) from some wider 'environment', and used to set-up access to
data-source(s) or whatever. Again, this aids understanding in knowing
where data has been created/gathered, and improves confidence when
utilising it down-the-line.

The other principle in only providing the required (eight) items of
data, is that the 'receiving-class' needs no understanding of the
structure or workings of the 'sending-class' = separation of concerns,
and each class has single purpose/reason to change.


A variation on that might be to use a method/function as the interface:

access = Access( config.access_data )

Thus, the config class (instance) will need an access_data method to
collate the data-items. The complimentary code in this Access.__init__(
self, config, ) might be something like:

(
self.source_name,
self.server_host,
self.server_port,
self.user_base,
self.user_identifier,
self.group_base,
self.group_identifier,
self.owner_base = config_access()
)

If you know my style/preferences, notice that I'm breaking my own 'rule'
of using named-parameters in preference to positional-parameters when
there are three or more. However, this *may* be one of those exceptions
(cf hobgoblins). That said, this is the third and least-preferred idea!

--
Regards,
=dn
--
https://mail.python.org/mailman/listinfo/python-list
Re: Configuring an object via a dictionary [ In reply to ]
On 3/15/2024 5:33 PM, Dan Sommers via Python-list wrote:
> On 2024-03-15 at 15:48:17 -0400,
> Thomas Passin via Python-list <python-list@python.org> wrote:
>
>> [...] And I suppose there is always the possibility that sometime in
>> the future an "or" clause like that will be changed to return a
>> Boolean, which one would expect anyway.
>
> Not only is the current value is way more useful, but changing it would
> be a compatibility and maintenance nightmare.

I'm with you here!

> If I want Java, I know where to find it. :-)

--
https://mail.python.org/mailman/listinfo/python-list
RE: Configuring an object via a dictionary [ In reply to ]
A part of the Python view of the world is about a concept of whether
something is "truthy" or not and thus many corners of the language do not
care what kind of object an expression returns. If the object is returned in
a context looking for not a Boolean value but a truth value, it is evaluated
and in other scenarios, left alone to propagate in the code.

Changing such behavior would be a very serious undertaking, and frankly,
silly.

But if anyone really wants an actual Boolean, then the non-not operator
should do the trick as !(whatever) takes what follows as a truthy value and
negates it and a second ! brings it back as a True/False as in !!(whatever)

And for many data types, perhaps all, you can use the bool() function that I
believe follows the same rules about being truthy.

Both of the above should be fairly easy to use in any rare contexts that
demand a more standard Boolean result as in some other languages.

It is one of many strengths of python that supports varieties of
polymorphism. And it allows a value to be passed or returned that can both
be viewed as some kind of object of many kinds and seen as a Boolean for
considerations like flow of control.

-----Original Message-----
From: Python-list <python-list-bounces+avi.e.gross=gmail.com@python.org> On
Behalf Of Dan Sommers via Python-list
Sent: Friday, March 15, 2024 5:33 PM
To: python-list@python.org
Subject: Re: Configuring an object via a dictionary

On 2024-03-15 at 15:48:17 -0400,
Thomas Passin via Python-list <python-list@python.org> wrote:

> [...] And I suppose there is always the possibility that sometime in
> the future an "or" clause like that will be changed to return a
> Boolean, which one would expect anyway.

Not only is the current value is way more useful, but changing it would
be a compatibility and maintenance nightmare.

If I want Java, I know where to find it. :-)
--
https://mail.python.org/mailman/listinfo/python-list

--
https://mail.python.org/mailman/listinfo/python-list
Re: Configuring an object via a dictionary [ In reply to ]
> On 15 Mar 2024, at 19:51, Thomas Passin via Python-list <python-list@python.org> wrote:
>
> I've always like writing using the "or" form and have never gotten bit

I, on the other hand, had to fix a production problem that using “or” introducted.
I avoid this idiom because it fails on falsy values.

Barry
--
https://mail.python.org/mailman/listinfo/python-list
Re: Configuring an object via a dictionary [ In reply to ]
Barry via Python-list schreef op 16/03/2024 om 9:15:
>
> > On 15 Mar 2024, at 19:51, Thomas Passin via Python-list <python-list@python.org> wrote:
> >
> > I've always like writing using the "or" form and have never gotten bit
>
> I, on the other hand, had to fix a production problem that using “or” introducted.
> I avoid this idiom because it fails on falsy values.
>
Me too. It's just too fragile. When writing code you're going to need an
alternative for cases where "config.get('source_name') or default_value"
doesn't work correctly; much better to use that alternative for all cases.

--
"This planet has - or rather had - a problem, which was this: most of the
people living on it were unhappy for pretty much of the time. Many solutions
were suggested for this problem, but most of these were largely concerned with
the movement of small green pieces of paper, which was odd because on the whole
it wasn't the small green pieces of paper that were unhappy."
-- Douglas Adams
--
https://mail.python.org/mailman/listinfo/python-list
Re: Configuring an object via a dictionary [ In reply to ]
On 3/16/2024 8:12 AM, Roel Schroeven via Python-list wrote:
> Barry via Python-list schreef op 16/03/2024 om 9:15:
>>
>> > On 15 Mar 2024, at 19:51, Thomas Passin via Python-list
>> <python-list@python.org>  wrote:
>> > > I've always like writing using the "or" form and have never gotten
>> bit
>>
>> I, on the other hand, had to fix a production problem that using “or”
>> introducted.
>> I avoid this idiom because it fails on falsy values.
>>
> Me too. It's just too fragile. When writing code you're going to need an
> alternative for cases where "config.get('source_name') or default_value"
> doesn't work correctly; much better to use that alternative for all cases.

Trying to remember when I've used it, that was probably on personal code
where I had a good idea what the values could be. Otherwise, I'm in
agreement.

--
https://mail.python.org/mailman/listinfo/python-list
Re: Configuring an object via a dictionary [ In reply to ]
On 16/03/24 21:15, Barry via Python-list wrote:
>
>
>> On 15 Mar 2024, at 19:51, Thomas Passin via Python-list <python-list@python.org> wrote:
>>
>> I've always like writing using the "or" form and have never gotten bit
>
> I, on the other hand, had to fix a production problem that using “or” introducted.
> I avoid this idiom because it fails on falsy values.

As with any other facility, one has to understand ALL implications!

It must be one of those intensely-frustrating errors to track-down,
which is then oh-so-simple to fix!

Are you able to list (real, if suitably anonymised) examples of where
the truthy/falsy was inappropriate, please?

--
Regards,
=dn
--
https://mail.python.org/mailman/listinfo/python-list
Re: Configuring an object via a dictionary [ In reply to ]
On 2024-03-16 08:15:19 +0000, Barry via Python-list wrote:
> > On 15 Mar 2024, at 19:51, Thomas Passin via Python-list <python-list@python.org> wrote:
> > I've always like writing using the "or" form and have never gotten bit
>
> I, on the other hand, had to fix a production problem that using “or” introducted.
> I avoid this idiom because it fails on falsy values.

Perl has a // operator (pronounced "err"), which works like || (or),
except that it tests whether the left side is defined (not None in
Python terms) instead of truthy. This still isn't bulletproof but I've
found it very handy.

hp

--
_ | Peter J. Holzer | Story must make more sense than reality.
|_|_) | |
| | | hjp@hjp.at | -- Charles Stross, "Creative writing
__/ | http://www.hjp.at/ | challenge!"
Re: Configuring an object via a dictionary [ In reply to ]
On 17/03/24 12:06, Peter J. Holzer via Python-list wrote:
> On 2024-03-16 08:15:19 +0000, Barry via Python-list wrote:
>>> On 15 Mar 2024, at 19:51, Thomas Passin via Python-list <python-list@python.org> wrote:
>>> I've always like writing using the "or" form and have never gotten bit
>>
>> I, on the other hand, had to fix a production problem that using “or” introducted.
>> I avoid this idiom because it fails on falsy values.
>
> Perl has a // operator (pronounced "err"), which works like || (or),
> except that it tests whether the left side is defined (not None in
> Python terms) instead of truthy. This still isn't bulletproof but I've
> found it very handy.


So, if starting from:

def method( self, name=None, ):

rather than:

self.name = name if name else default_value

ie

self.name = name if name is True else default_value


the more precise:

self.name = name if name is not None or default_value

or:

self.name = default_value if name is None or name

because "is" checks for identity, whereas "==" and True-thy encompass a
range of possible alternate values?

--
Regards,
=dn
--
https://mail.python.org/mailman/listinfo/python-list
Re: Configuring an object via a dictionary [ In reply to ]
On 2024-03-17 17:15:32 +1300, dn via Python-list wrote:
> On 17/03/24 12:06, Peter J. Holzer via Python-list wrote:
> > On 2024-03-16 08:15:19 +0000, Barry via Python-list wrote:
> > > > On 15 Mar 2024, at 19:51, Thomas Passin via Python-list <python-list@python.org> wrote:
> > > > I've always like writing using the "or" form and have never gotten bit
> > >
> > > I, on the other hand, had to fix a production problem that using “or” introducted.
> > > I avoid this idiom because it fails on falsy values.
> >
> > Perl has a // operator (pronounced "err"), which works like || (or),
> > except that it tests whether the left side is defined (not None in
> > Python terms) instead of truthy. This still isn't bulletproof but I've
> > found it very handy.
>
>
> So, if starting from:
>
> def method( self, name=None, ):
>
> rather than:
>
> self.name = name if name else default_value
>
> ie
>
> self.name = name if name is True else default_value

These two lines don't have the same meaning (for the reason you outlined
below). The second line is also not very useful.



> the more precise:
>
> self.name = name if name is not None or default_value
>
> or:
>
> self.name = default_value if name is None or name

Those are syntax errors. I think you meant to write "else" instead of
"or".

Yes, exactly. That's the semantic of Perl's // operator.

JavaScript has a ?? operator with similar semantics (slightly
complicated by the fact that JavaScript has two "nullish" values).

hp

--
_ | Peter J. Holzer | Story must make more sense than reality.
|_|_) | |
| | | hjp@hjp.at | -- Charles Stross, "Creative writing
__/ | http://www.hjp.at/ | challenge!"
Re: Configuring an object via a dictionary [ In reply to ]
On 18/03/24 04:11, Peter J. Holzer via Python-list wrote:
> On 2024-03-17 17:15:32 +1300, dn via Python-list wrote:
>> On 17/03/24 12:06, Peter J. Holzer via Python-list wrote:
>>> On 2024-03-16 08:15:19 +0000, Barry via Python-list wrote:
>>>>> On 15 Mar 2024, at 19:51, Thomas Passin via Python-list <python-list@python.org> wrote:
>>>>> I've always like writing using the "or" form and have never gotten bit
>>>>
>>>> I, on the other hand, had to fix a production problem that using “or” introducted.
>>>> I avoid this idiom because it fails on falsy values.
>>>
>>> Perl has a // operator (pronounced "err"), which works like || (or),
>>> except that it tests whether the left side is defined (not None in
>>> Python terms) instead of truthy. This still isn't bulletproof but I've
>>> found it very handy.
>>
>>
>> So, if starting from:
>>
>> def method( self, name=None, ):
>>
>> rather than:
>>
>> self.name = name if name else default_value
>>
>> ie
>>
>> self.name = name if name is True else default_value
>
> These two lines don't have the same meaning (for the reason you outlined
> below). The second line is also not very useful.
>
>
>
>> the more precise:
>>
>> self.name = name if name is not None or default_value
>>
>> or:
>>
>> self.name = default_value if name is None or name
>
> Those are syntax errors. I think you meant to write "else" instead of
> "or".
>
> Yes, exactly. That's the semantic of Perl's // operator.
>
> JavaScript has a ?? operator with similar semantics (slightly
> complicated by the fact that JavaScript has two "nullish" values).


Thanks Peter!
(yes, sad consequences of suffering a neighbor's party-til-midnight
followed by an 0530 meeting-start - children: don't code exhausted!)


Herewith, an illustration of how the corrected version of the above
works - and how (in what seem unusual cases) it avoids any truthy/falsy
confusion, as raised earlier in this thread:

>>> default_value = "default"
>>> name = "Fred Flintstone"
>>> name if name is not None else default_value
'Fred Flintstone'

>>> name = None
>>> name if name is not None else default_value
'default'
>>> name = False
>>> name if name is not None else default_value
False
>>> name = 1
>>> name if name is not None else default_value
1
>>> name = 0
>>> name if name is not None else default_value
0


Personally: I find the above coding more logical, because our primary
interest is on 'the happy case', ie where the value has been assigned
(to "name"); and the default_value is only applied as a "guard".


On the other hand, I dislike the not-condition because it forces me to
think (and maybe dust-off DeMorgan). Accordingly:

>>> name = "Fred Flintstone"
>>> default_value if name is None else name
'Fred Flintstone'
>>> name = None
>>> default_value if name is None else name
'default'
>>> name = False
>>> default_value if name is None else name
False
...


YMMV!
NB your corporate Style Guide may prefer 'the happy path'...

--
Regards,
=dn
--
https://mail.python.org/mailman/listinfo/python-list
RE: Configuring an object via a dictionary [ In reply to ]
If we are bringing up other languages, let's return to what was part of the original question.

How van a dictionary be used in python if your goal is to sort of use it to instantiate it into a set of variables and values inside the local or global or other namespaces? Can we learn anything from other programming languages with other paradigms?

I was thinking that in one sense, python has other kinds of collections such as a class or class member with internal variables that also has some features similar enough.

I mean if I were to call a function with one argument being a dictionary like {"a":1, "c": 3} where in theory any number of similar key value pairs might exist or not be present, then the question sounded like a request to create variables, in this case a, and c, that hold those values. I will not debate the wisdom of doing that, of course. Nor will I debate what should happen if some of those variable names are already in use. OK?

There are ways to do this albeit some are a tad obscure such as taking a printable representation of the dictionary and editing it and feeding that to an eval.

But could you serve a similar purpose by passing an object containing varying internal fields (or methods) and values including some of the dataclasses Dave Neal is highlighting? Is there some overlap with using dictionaries?

In Javascript, I would say they have a very different view in which all kinds of things overlap and their objects can even implement what others might call arrays albeit a tad weirder like allowing missing indices and ignoring non-numeric indices for some purposes. Someone may wish to chime in if people can and do take such objects passed in and split them into individual variables as requested.

What I wanted to mention is some philosophical issues in the R language in which the default language used other data structures to loosely support what dictionaries often do in python. They have named lists where components optionally can have a name as in list(a=5, "b"=6, 7, "hello world") and they have data structures called environments. Loosely, an environment is a set of name=value pairs and is pretty much like a dictionary and is mostly used behind the scenes as the interpreter searches for variable names in a sequence of environments such as the parent environment. But you can use environments all over the place on purpose and as noted, a named list simulates an environment.

So a fairly common usage when using base R is to take a data.frame (which is at first approximation a list of named vectors all the same length) and want to work with the column names without extra typing. If I had a data.frame that looked like mydf <- data.frame(a=1:3, b=3:1) then if I wanted to add corresponding entries, I might type:

result <- mydf$a + mydf$b

inside a with statement, an environment is put on the stack consisting of the contents of mydf and you can now use things like:

result <- with(mydf, a+b)

There is more but the point is for those who hate the extra typing of long names, this can be useful and a tad dangerous if the variable names are not unique.

But as R delays evaluation in various ways, a similar style has evolved in an assortment of packages to the point where I often program in a style that looks like:

result <-
mydf |>
mutate(newcol = a+b, doubled = 2*newcol, ...)

The point is that all kinds of things that seem like local variables can be used as if they had been declared withing some environment but that are not available once you leave that region.

So perhaps there is some validity in a request to be able to just pass an argument as a dictionary in python and have it unpacked.

In actuality, I wonder if the OP is aware of the unpacking functionality you can get using **dict in a function invocation.

Say you have a function that expects any combination of three variables called the unoriginal names of alpha/beta/gamma and you want to call it with a dictionary that contains any subset of those same keys and nothing else:

mydict = {"alpha":5, "beta":6}

def unpacked(alpha=None, beta=None, gamma=None):
print(alpha if alpha != None else "No alpha")
print(beta if beta != None else "No beta")
print(gamma if gamma != None else "No gamma")

If I now call unpacked with ** mydict:

>>> unpacked(**mydict)
5
6
No gamma

Within the body of that function, arguably, I can tell if something was passed or not, assuming None or any other sentinel I choose is not used except in setting the default.

And if you add other unknown names, like delta, they seem to be ignored without harmful effects or can be caught in other ways.

>>> unpacked({"gamma":7, "delta":8})
{'gamma': 7, 'delta': 8}
No beta
No gamma

So given a function similar to this, and you wanting LOCAL variables set, how would this do if you wanted these three or any number, and in a row:

mydict = {"beta":6, "alpha":5}

def dict_to_vars(alpha=None, beta=None, gamma=None):
return(alpha, beta, gamma)

alpha, beta, gamma = dict_to_vars(**mydict)

The result works no matter what order you have added to the dictionary as long as you know exactly which N you want. Obviously, you may not be thrilled with None as a value and can add code to remove empty variables after they have been instantiated.

>>> alpha
5
>>> beta
6
>>> gamma
>>>

If you want to ignore some entries, use _ in your list of places for items you want to toss.

alpha, _, _ = dict_to_vars(**mydict)

The above is really just keeping alpha.

Of course if the possible keys are not known in advance, this does not work but other languages that allow this may be better for your purpose.



-----Original Message-----
From: Python-list <python-list-bounces+avi.e.gross=gmail.com@python.org> On Behalf Of Peter J. Holzer via Python-list
Sent: Sunday, March 17, 2024 11:12 AM
To: python-list@python.org
Subject: Re: Configuring an object via a dictionary

On 2024-03-17 17:15:32 +1300, dn via Python-list wrote:
> On 17/03/24 12:06, Peter J. Holzer via Python-list wrote:
> > On 2024-03-16 08:15:19 +0000, Barry via Python-list wrote:
> > > > On 15 Mar 2024, at 19:51, Thomas Passin via Python-list <python-list@python.org> wrote:
> > > > I've always like writing using the "or" form and have never gotten bit
> > >
> > > I, on the other hand, had to fix a production problem that using “or” introducted.
> > > I avoid this idiom because it fails on falsy values.
> >
> > Perl has a // operator (pronounced "err"), which works like || (or),
> > except that it tests whether the left side is defined (not None in
> > Python terms) instead of truthy. This still isn't bulletproof but I've
> > found it very handy.
>
>
> So, if starting from:
>
> def method( self, name=None, ):
>
> rather than:
>
> self.name = name if name else default_value
>
> ie
>
> self.name = name if name is True else default_value

These two lines don't have the same meaning (for the reason you outlined
below). The second line is also not very useful.



> the more precise:
>
> self.name = name if name is not None or default_value
>
> or:
>
> self.name = default_value if name is None or name

Those are syntax errors. I think you meant to write "else" instead of
"or".

Yes, exactly. That's the semantic of Perl's // operator.

JavaScript has a ?? operator with similar semantics (slightly
complicated by the fact that JavaScript has two "nullish" values).

hp

--
_ | Peter J. Holzer | Story must make more sense than reality.
|_|_) | |
| | | hjp@hjp.at | -- Charles Stross, "Creative writing
__/ | http://www.hjp.at/ | challenge!"

--
https://mail.python.org/mailman/listinfo/python-list
Re: Configuring an object via a dictionary [ In reply to ]
Tobiah <toby@tobiah.org> writes:

> I should mention that I wanted to answer your question,
> but I wouldn't actually do this. I'd rather opt for
> your self.config = config solution. The config options
> should have their own namespace.
>
> I don't mind at all referencing foo.config['option'],
> or you could make foo.config an object by itself so
> you can do foo.config.option. You'd fill it's attributes
> in the same way I suggested for your main object.

Thanks for the thoughts. I'll go for self.config = config after
all, since, as you say, the clutter caused by the referencing is not
that significant.

Cheers,

Loris

--
This signature is currently under constuction.
--
https://mail.python.org/mailman/listinfo/python-list
Re: Configuring an object via a dictionary [ In reply to ]
dn wrote:
>Loris Bennett wrote:
>> However, with a view to asking forgiveness rather than
>> permission, is there some simple way just to assign the dictionary
>> elements which do in fact exist to self-variables?
>
>Assuming config is a dict:
>
> self.__dict__.update( config )

Here's another approach:

config_defaults = dict(
server_host='localhost',
server_port=443,
# etc.
)
...
def __init__(self, config):
self.conf = types.SimpleNamespace(**{**config_defaults, **config})

This gives you defaults, simple attribute access, and avoids the risk of name collisions that you get when updating __dict__.

Using a dataclass may be better:

@dataclasses.dataclass
class Settings:
group_base : str
server_host : str = 'localhost'
server_port : int = 443
...
def __init__(self, config):
self.conf = Settings(**config)

regards, Anders

--
https://mail.python.org/mailman/listinfo/python-list
Re: Configuring an object via a dictionary [ In reply to ]
It's too complicated, there's no need for this
def __init__(self, config):

self.__dict__ = config
self.connection = None
"""
other code .....
"""
Note that you need to keep the fields in the config dict named the same as
the fields you want to be assigned to in your class

Loris Bennett via Python-list <python-list@python.org> ?2024?3?19???
01:39???

> Tobiah <toby@tobiah.org> writes:
>
> > I should mention that I wanted to answer your question,
> > but I wouldn't actually do this. I'd rather opt for
> > your self.config = config solution. The config options
> > should have their own namespace.
> >
> > I don't mind at all referencing foo.config['option'],
> > or you could make foo.config an object by itself so
> > you can do foo.config.option. You'd fill it's attributes
> > in the same way I suggested for your main object.
>
> Thanks for the thoughts. I'll go for self.config = config after
> all, since, as you say, the clutter caused by the referencing is not
> that significant.
>
> Cheers,
>
> Loris
>
> --
> This signature is currently under constuction.
> --
> https://mail.python.org/mailman/listinfo/python-list
>
--
https://mail.python.org/mailman/listinfo/python-list
Re: Configuring an object via a dictionary [ In reply to ]
Op 19/03/2024 om 0:44 schreef Gilmeh Serda via Python-list:
> On Mon, 18 Mar 2024 10:09:27 +1300, dn wrote:
>
> > YMMV!
> > NB your corporate Style Guide may prefer 'the happy path'...
>
> If you only want to check for None, this works too:
>
> >>> name = None
> >>> dafault_value = "default"
> >>> name or default_value
> 'default'
> >>> name = 'Fred Flintstone'
> >>> name or default_value
> 'Fred Flintstone'

>>> name = ''
>>> name or default_value
'default'

>>> name = False
>>> name or default_value
'default'

>>> name = []
>>> name or default_value
'default'

>>> name = 0
>>> name or default_value
'default'

You haven't only checked for None! You have rejected *every* falsish
value, even though they may very well be acceptable values.

--
"Most of us, when all is said and done, like what we like and make up
reasons for it afterwards."
-- Soren F. Petersen

--
https://mail.python.org/mailman/listinfo/python-list
Re: Configuring an object via a dictionary [ In reply to ]
On 2024-03-20 at 09:49:54 +0100,
Roel Schroeven via Python-list <python-list@python.org> wrote:

> You haven't only checked for None! You have rejected *every* falsish value,
> even though they may very well be acceptable values.

OTOH, only you can answer these questions about your situations.

Every application, every item of configuration data, is going to be a
little bit different.

What, exactly, does "missing" mean? That there's no entry in a config
file? That there's some sort of degenerate entry with "missing"
semantics (e.g. a line in a text file that contains the name of the
value and an equals sign, but no value)? An empty string or list? Are
you making your program easier for users to use, easier for testers to
test, easier for authors to write and to maintain, or something else?
What is your program allowed and not allowed to do in the face of
"missing" configuration data?

Once you've nailed down the semantics of the configuration data, then
the code usually falls out pretty quickly. But arguing about corner
cases and failure modes without specifications is a losing battle.
Every piece of code is suspect unless you know what the inputs mean, and
what the application "should" do if the don't look like that.

Python's flexibiliry and expressiveness are double edge swords. Use
them wisely. :-)

Sorry for the rant.

Carry on.
--
https://mail.python.org/mailman/listinfo/python-list