Set a flag on the function or a global?

classic Classic list List threaded Threaded
37 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Set a flag on the function or a global?

Ron Adam-2


On 06/16/2015 05:15 AM, Steven D'Aprano wrote:

> On Tuesday 16 June 2015 10:24, Ron Adam wrote:
>
>> >Another way is to make it an object with a __call__ method.
>> >
>> >The the attribute can be accessed from both outside and inside dependably.
> That's what functions are, objects with a __call__ method:
>
>
> py> (lambda: None).__call__
> <method-wrapper '__call__' of function object at 0xb7301a04>

Yes ;-)


> One slight disadvantage is that functions don't take a "self" parameter by
> default, which means they have to refer to themselves by name:
>
> def spam():
>      print spam.attr
>
>
> Here's a fun hack:
>
> py> from types import MethodType
> py> def physician(func):
> ...     # As in, "Physician, Know Thyself":-)
> ...     return MethodType(func, func)
> ...
> py> @physician
> ... def spam(this, n):
> ...     return this.food * n
> ...
> py> spam.__func__.food = "spam-and-eggs "
> py> spam(3)
> 'spam-and-eggs spam-and-eggs spam-and-eggs'


How about this?:  (paste it into your console)

#---------------------
import sys
class EDir:
     long = False
     def __call__(self, obj=None):
         if obj == None:
             d = sys._getframe(1).f_locals
         else:
             d = dir(obj)
         return [x for x in d if self.long or not x.startswith("_")]


edir = EDir()

edir()

edir(edir)

edir.long = True
edir(edir)

edir.long = False
edir(edir)
#----------------------


I didn't test how that works from other modules or in nested scopes.  Also
replacing None with a unique sentinel object may be better so dir(None)
will work.

Cheers,
    Ron








Reply | Threaded
Open this post in threaded view
|

Set a flag on the function or a global?

Oscar Benjamin-2
In reply to this post by Steven D'Aprano-11
On 16 June 2015 at 09:18, Steven D'Aprano
<steve+comp.lang.python at pearwood.info> wrote:

>
> The primary use-case (at least *my* use-case, and hopefully others) is to
> have "from module import edir as dir" in their Python startup file. That
> means that when running interactively, they will get the enhanced version of
> dir, but when running a script or an application they'll just get the
> regular one.
>
> (Ideally, the regular one will eventually gain the same superpowers as edir
> has, but that's a discussion for another day.)
>
> Besides, apart from the inspect module, which probably shouldn't, who uses
> dir() programmatically?
>
> (If you do, you'll be glad to hear that edir() behaves the same as regular
> dir() by default.)

What's the point in giving edir two modes if one of them is the same
as dir? You could just do "from module import edir" and then use
dir/edir as desired.

Personally I just use ipython's tab-completion instead of dir. It
shows the dunders if you first type underscores but hides them
otherwise e.g.:

In [1]: a = []

In [2]: a.<tab>
a.append   a.count    a.extend   a.index    a.insert   a.pop
a.remove   a.reverse  a.sort

In [2]: a.__<tab>
a.__add__           a.__format__        a.__imul__          a.__new__
         a.__setslice__
a.__class__         a.__ge__            a.__init__
a.__reduce__        a.__sizeof__
a.__contains__      a.__getattribute__  a.__iter__
a.__reduce_ex__     a.__str__
a.__delattr__       a.__getitem__       a.__le__            a.__repr__
         a.__subclasshook__
a.__delitem__       a.__getslice__      a.__len__           a.__reversed__
a.__delslice__      a.__gt__            a.__lt__            a.__rmul__
a.__doc__           a.__hash__          a.__mul__           a.__setattr__
a.__eq__            a.__iadd__          a.__ne__            a.__setitem__


--
Oscar

Reply | Threaded
Open this post in threaded view
|

Set a flag on the function or a global?

Michael Torrie
In reply to this post by Ben Finney-10
On 06/15/2015 06:20 PM, Ben Finney wrote:
> I'm surprised by your assertion. To my mind, outside callers get simple
> and direct access to the attribute, whereas the code of the function
> itself does not have such easy access; unlike ?self? for the current
> instance of a class, there's no obvious name to use for referring to the
> function object within the function object's own code.

Of course it has access, and it's obvious and easy as well:

def foo():
    foo.flag = True

The only thing I'm not sure of is a clean way to test for the
attribute's existence.  The __init__.py of a package, or the
initialization code of the module could preset the attribute, though.


Reply | Threaded
Open this post in threaded view
|

Set a flag on the function or a global?

Michael Torrie
In reply to this post by Ethan Furman-2
On 06/15/2015 06:19 PM, Ethan Furman wrote:
> Setting a global on the module (which I may not have, and probably
> didn't, import) for only one function is overkill.

What do you mean?  Even if you pull in just one function from the
module on an import, the module's initialization code still runs.





Reply | Threaded
Open this post in threaded view
|

Set a flag on the function or a global?

Peter Otten
In reply to this post by Steven D'Aprano-11
Steven D'Aprano wrote:

> I have a function in a module which is intended to be used by importing
> that name alone, then used interactively:
>
>     from module import edir
>     edir(args)
>
>
> edir is an enhanced version of dir, and one of the enhancements is that
> you can filter out dunder methods. I have reason to believe that people
> are split on their opinion on whether dunder methods should be shown by
> default or not: some people want to see them, others do not. Since edir
> is meant to be used interactively, I want to give people a setting to
> control whether they get dunders by default or not.
>
> I have two ideas for this, a module-level global, or a flag set on the
> function object itself. Remember that the usual way of using this will be
> "from module import edir", there are two obvious ways to set the global:
>
> import module
> module.dunders = False
>
> # -or-
>
> edir.__globals__['dunders'] = False
>
>
> Alternatively, I can use a flag set on the function object itself:
>
> edir.dunders = False
>
>
> Naturally you can always override the default by explicitly specifying a
> keyword argument edir(obj, dunders=flag).
>
> Thoughts and feedback? Please vote: a module global, or a flag on the
> object? Please give reasons, and remember that the function is intended
> for interactive use.

"""
In general I'm wary of routines that take flags (such as 'swapped')
that really mean "use a different version of the function" -- these
flags are almost always passed from constants and so it would be more
efficient to have a second name for the variant function.
"""
http://legacy.python.org/search/hypermail/python-1994q2/0852.html



Reply | Threaded
Open this post in threaded view
|

Set a flag on the function or a global?

Ethan Furman-2
In reply to this post by Michael Torrie
On 06/16/2015 06:56 AM, Michael Torrie wrote:
> On 06/15/2015 06:19 PM, Ethan Furman wrote:
>>
>> Setting a global on the module (which I may not have, and probably
>> didn't, import) for only one function is overkill.
>
> What do you mean?  Even if you pull in just one function from the
> module on an import, the module's initialization code still runs.

For me, an appropriate use of globals requires that it will be used by many other pieces of code in that module.  If only one piece of code is using it, it's not really global.

--
~Ethan~

Reply | Threaded
Open this post in threaded view
|

Set a flag on the function or a global?

Steven D'Aprano-11
In reply to this post by Steven D'Aprano-11
On Tue, 16 Jun 2015 13:45:01 +0100, Oscar Benjamin wrote:

> On 16 June 2015 at 09:18, Steven D'Aprano
> <steve+comp.lang.python at pearwood.info> wrote:
>>
>> The primary use-case (at least *my* use-case, and hopefully others) is
>> to have "from module import edir as dir" in their Python startup file.
>> That means that when running interactively, they will get the enhanced
>> version of dir, but when running a script or an application they'll
>> just get the regular one.
>>
>> (Ideally, the regular one will eventually gain the same superpowers as
>> edir has, but that's a discussion for another day.)
>>
>> Besides, apart from the inspect module, which probably shouldn't, who
>> uses dir() programmatically?
>>
>> (If you do, you'll be glad to hear that edir() behaves the same as
>> regular dir() by default.)
>
> What's the point in giving edir two modes if one of them is the same as
> dir? You could just do "from module import edir" and then use dir/edir
> as desired.

Oh ye of little faith :-)

I practically live in the Python interactive interpreter, and this has
evolved from functionality I have wished dir has. It's a lot more than
just whether or not dunders are displayed by default.

The most important feature of edir() is that it supports globbing and
substring matches:


py> dir("", "low")
['islower', 'lower']
py> dir('', '*er')
['center', 'isidentifier', 'islower', 'isupper', 'lower', 'upper']


By default, globs are case-insensitive, but you can force them to be case-
sensitive with a metachar. I added this because I kept coming across
classes with methods that used CamelCase when I expected lowercase. It
understands the standard metacharacters ? * and [] as well as prefixes !
to invert the match and = to force case-sensitivity.

It also optionally includes the metaclass of the object. This was
requested by somebody else as standard behaviour for dir, on python-ideas
and the bug tracker, but rejected.

The reason this was requested is that dir() intentionally doesn't return
all the attributes visible from an object:

py> "mro" in dir(str)
False
py> str.mro
<built-in method mro of type object at 0x81db360>


I thought it seemed like an interesting idea, and added it as an option
with a keyword-only argument:

py> "mro" in dir(str, meta=True)
True


but frankly I'm not entirely sure this feature is useful. Time will tell.


> Personally I just use ipython's tab-completion instead of dir. It shows
> the dunders if you first type underscores but hides them otherwise e.g.:

Tab completion is great, but it solves a different problem. One with some
overlap, admittedly, but still different.


--
Steven D'Aprano

Reply | Threaded
Open this post in threaded view
|

Set a flag on the function or a global?

Cameron Simpson
In reply to this post by Steven D'Aprano-11
On 16Jun2015 18:18, Steven D'Aprano <steve+comp.lang.python at pearwood.info> wrote:
>On Tuesday 16 June 2015 10:35, MRAB wrote:
>> On 2015-06-16 01:24, sohcahtoa82 at gmail.com wrote:
>>> Using a keyword argument for the edir function is the most intuitive
>>> and easy to read, IMO.
>
>edir() has a keyword argument: edir(x, dunders=False) suppresses the return
>of dunder names. But since the primary purpose of [e]dir is, in my opinion,
>to be used interactively, needing to type an extra 15 characters to hide
>dunders is too inconvenient.

I'm just catching up here, but have now read the whole thread.

I am personally against a global (==> single variable affecting all callers) of
any kind, be it a function attribute or whatever. Why? For that same reason
that we discourage use of functions like os.chdir or os.umask except in rare
circumstances: the single call inevitably affects the entire program behaviour.

Your intended use case may be interactive, where I agree the convenience looks
nice. However, I think it inevitable that someone imports your edir function as
an aid to writing friendly debugging messages is a larger program, and thus is
escapes into noninteractive use. (As an example, I have a class named "O" which
I use as a base class for many classes, especially in the debugging phase; its
major semantic is friendlier __str__ and __repr__ for exactly the same reasons
you wrote "edir", and with similar effect.)

On that basis (avoiding global state) my preference would be strongly to rely
entirely on your keyword argument (dunder=False) and to offer two flavors, such
as the "edir" and "edir_" suggested elsewhere. The user can always import
"edir_ as edir_noisy" if they are of the mindset which dislikes single trailing
underscores.

[...]
>> But it's meant to be used interactively. If they're using it in a
>> script, they'd most likely set the argument appropriately.
>
>Yes.

Ideally yes. "Most likely"? I have less confidence there.

>The primary use-case (at least *my* use-case, and hopefully others) is to
>have "from module import edir as dir" in their Python startup file. That
>means that when running interactively, they will get the enhanced version of
>dir, but when running a script or an application they'll just get the
>regular one.

This fits well with two functions, then they can import "edir" or "edir_" as
dir as they see fit.

>Besides, apart from the inspect module, which probably shouldn't, who uses
>dir() programmatically?

Directly, perhaps rarely. But I use my O.__str__ method implicitly quite a lot,
and it has a similar purpose to your edir. (It is not the same, so the parallel
is not perfect.)

Cheers,
Cameron Simpson <cs at zip.com.au>

All who love Liberty are enemies of the State.  - Karl Hess

Reply | Threaded
Open this post in threaded view
|

Set a flag on the function or a global?

Steven D'Aprano-8
In reply to this post by Steven D'Aprano-11
On Wed, 17 Jun 2015 09:51 am, Cameron Simpson wrote:

> On 16Jun2015 18:18, Steven D'Aprano <steve+comp.lang.python at pearwood.info>
> wrote:
>>On Tuesday 16 June 2015 10:35, MRAB wrote:
>>> On 2015-06-16 01:24, sohcahtoa82 at gmail.com wrote:
>>>> Using a keyword argument for the edir function is the most intuitive
>>>> and easy to read, IMO.
>>
>>edir() has a keyword argument: edir(x, dunders=False) suppresses the
>>return of dunder names. But since the primary purpose of [e]dir is, in my
>>opinion, to be used interactively, needing to type an extra 15 characters
>>to hide dunders is too inconvenient.
>
> I'm just catching up here, but have now read the whole thread.

Thanks for doing so before commenting :-)


> I am personally against a global (==> single variable affecting all
> callers) of any kind, be it a function attribute or whatever. Why? For
> that same reason that we discourage use of functions like os.chdir or
> os.umask except in rare circumstances: the single call inevitably affects
> the entire program behaviour.

Well yes. And I would normally agree. But I think a better analogy here is
with print. By default, print outputs to stdout. You can customise that by
giving an extra argument to print:


print(spam, file=sys.stderr)  # Python 3
print >>sys.stderr, spam  # Python 2


or you can re-direct sys.stdout. Both are allowed. But doing the later is
potentially more disruptive, and should be done with care. Nevertheless, it
is supported, and Python will set up stdout to the appropriate file for you
when you start.

Although I think you're being overly cautious, I've changed the behaviour:

- the default is dunders=True, always;

- you can manually override the default with an optional, explicit dunders
keyword-only argument, always;

- you can change the default to dunders=False by setting an attribute, only
when running in interactive mode.



--
Steven


Reply | Threaded
Open this post in threaded view
|

Set a flag on the function or a global?

Laura Creighton-2
To figure out what I like, I would need to play with edir, and the
suite that it comes with.

I suspect there is command like:

stop_showing_me_all_this_uninteresting_stuff = True

in my future, and dunders is only a small part of that.

Laura


Reply | Threaded
Open this post in threaded view
|

Set a flag on the function or a global?

Steven D'Aprano-8
In reply to this post by Steven D'Aprano-8
On Thu, 18 Jun 2015 01:06 am, Laura Creighton wrote:

> To figure out what I like, I would need to play with edir, and the
> suite that it comes with.
>
> I suspect there is command like:
>
> stop_showing_me_all_this_uninteresting_stuff = True
>
> in my future, and dunders is only a small part of that.


The full signature is:

edir([object [, glob=''] [, dunders=True] [, meta=False]])

All four arguments are optional, and dunders and meta are keyword-only. It
is intended to work with Python 2.4 or better. It may not work in Jython on
IronPython, depending on whether or not sys._getframe is supported.

The glob argument is the most interesting, in my opinion. If you give a
non-empty glob, only names matching that glob are returned:

py> dir('', 'is*')
['isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper']


If the glob contains no metacharacters, a plain substring match is done:

py> dir('', 'up')
['isupper', 'upper']


Matches are case-insensitive by default, but can be made case-sensitive by
using a prefix "=". To invert the match (return names which don't match),
use "!" as the prefix. You can use "!=" as a combined prefix.

I haven't yet released this as an independent package as yet, but you can
find it here:

https://code.google.com/p/my-startup-file/

in the "enhanced_dir.py" module.

Consider it beta quality and free for personal use; if anyone wishes a more
formal licence, either wait until I publish it independently, or contact me
off list.




--
Steven


Reply | Threaded
Open this post in threaded view
|

Documenting a function signature (was: Set a flag on the function or a global?)

Ben Finney-10
Steven D'Aprano <steve at pearwood.info> writes:

> The full signature is:
>
> edir([object [, glob=''] [, dunders=True] [, meta=False]])
>
> All four arguments are optional, and dunders and meta are
> keyword-only.

The official documentation seems to prefer this style::

    edit(object, glob='', *, dunders=True, meta=False)

(I think that's the style, anyway.)

I like that it gets rid of square brackets; with the notation showing a
default value, that already communicates that a parameter is optional.

Since the introduction of keyword-only arguments in Python functions,
the question arises of how to communicate this in documentation.

The lone asterisk showing the separation of keyword-only arguments from
the rest is confusing to me. Not least because it is (if I understand
correctly) invalid syntax to actually have that in Python code.

What are your thoughts, dear reader, on the documentation style for
showing a Python function signature, now that we have not only default
arguments but also keyword-only arguments?

--
 \      ?It is difficult to get a man to understand something when his |
  `\   salary depends upon his not understanding it.? ?Upton Sinclair, |
_o__)                                                             1935 |
Ben Finney


Reply | Threaded
Open this post in threaded view
|

Documenting a function signature (was: Set a flag on the function or a global?)

Ian Kelly-2
On Wed, Jun 17, 2015 at 6:04 PM, Ben Finney <ben+python at benfinney.id.au> wrote:

> Steven D'Aprano <steve at pearwood.info> writes:
>
>> The full signature is:
>>
>> edir([object [, glob=''] [, dunders=True] [, meta=False]])
>>
>> All four arguments are optional, and dunders and meta are
>> keyword-only.
>
> The official documentation seems to prefer this style::
>
>     edit(object, glob='', *, dunders=True, meta=False)
>
> (I think that's the style, anyway.)
>
> I like that it gets rid of square brackets; with the notation showing a
> default value, that already communicates that a parameter is optional.
>
> Since the introduction of keyword-only arguments in Python functions,
> the question arises of how to communicate this in documentation.
>
> The lone asterisk showing the separation of keyword-only arguments from
> the rest is confusing to me. Not least because it is (if I understand
> correctly) invalid syntax to actually have that in Python code.

No, this is valid syntax in Python 3. It means the same thing to the
compiler -- the remaining arguments are keyword-only -- that it means
in the documentation.

Reply | Threaded
Open this post in threaded view
|

Documenting a function signature (was: Set a flag on the function or a global?)

Chris Angelico
In reply to this post by Ben Finney-10
On Thu, Jun 18, 2015 at 10:04 AM, Ben Finney <ben+python at benfinney.id.au> wrote:

> Steven D'Aprano <steve at pearwood.info> writes:
>
>> The full signature is:
>>
>> edir([object [, glob=''] [, dunders=True] [, meta=False]])
>>
>> All four arguments are optional, and dunders and meta are
>> keyword-only.
>
> The official documentation seems to prefer this style::
>
>     edit(object, glob='', *, dunders=True, meta=False)

Mostly. That would imply that object is a mandatory parameter, which
AIUI isn't the case for Steven's edir. The downside of this kind of
signature is that it's hard to show the parameters that have unusual
defaults (either sentinel objects and custom code to cope, or they're
pulled out of *a or **kw).

> The lone asterisk showing the separation of keyword-only arguments from
> the rest is confusing to me. Not least because it is (if I understand
> correctly) invalid syntax to actually have that in Python code.

It's perfectly valid, and it does exactly what it looks like. What's
not valid syntax is the slash that comes up in some Argument Clinic
functions:

>>> help(list.__init__)
__init__(self, /, *args, **kwargs)
    Initialize self.  See help(type(self)) for accurate signature.

> What are your thoughts, dear reader, on the documentation style for
> showing a Python function signature, now that we have not only default
> arguments but also keyword-only arguments?

I like the executable form when it's fairly simple. If you have a
boolean that defaults to True but you can set it to false, saying
"dunders=True" is awesomely clear. It's a bit harder when you want to
do something more complicated, but so far as it's possible, I'd like
to see signatures that look like they were copied and pasted straight
from the function definition.

ChrisA

Reply | Threaded
Open this post in threaded view
|

Documenting a function signature (was: Set a flag on the function or a global?)

random832@fastmail.us
On Wed, Jun 17, 2015, at 20:14, Chris Angelico wrote:
> Mostly. That would imply that object is a mandatory parameter, which
> AIUI isn't the case for Steven's edir. The downside of this kind of
> signature is that it's hard to show the parameters that have unusual
> defaults (either sentinel objects and custom code to cope, or they're
> pulled out of *a or **kw).

My instinct would be to put =... for that case and explain in the
documentation "If foo is not provided, [behavior]".

Of course, that runs the risk of people thinking you actually mean
Ellipsis, or that providing Ellipsis explicitly will give the same
behavior as the default.

Reply | Threaded
Open this post in threaded view
|

Documenting a function signature (was: Set a flag on the function or a global?)

Laura Creighton-2
In reply to this post by Ben Finney-10
In a message of Thu, 18 Jun 2015 10:04:46 +1000, Ben Finney writes:
>Since the introduction of keyword-only arguments in Python functions,
>the question arises of how to communicate this in documentation.

I suppose it is way too late to scream "I hate keyword-only arguments"!

>The lone asterisk showing the separation of keyword-only arguments from
>the rest is confusing to me. Not least because it is (if I understand
>correctly) invalid syntax to actually have that in Python code.

Me too.

Laura


Reply | Threaded
Open this post in threaded view
|

Documenting a function signature

Ben Finney-10
Laura Creighton <lac at openend.se> writes:

> In a message of Thu, 18 Jun 2015 10:04:46 +1000, Ben Finney writes:
> >Since the introduction of keyword-only arguments in Python functions,
> >the question arises of how to communicate this in documentation.
>
> I suppose it is way too late to scream "I hate keyword-only
> arguments"!

It's never too late for screams, go right ahead.

If you're hoping those screams will result in the keyword-arguments not
being part of Python now and in the future; yes, I think it's too late
for that :-)

--
 \      ?If sharing a thing in no way diminishes it, it is not rightly |
  `\      owned if it is not shared.? ?Augustine of Hippo (354?430 CE) |
_o__)                                                                  |
Ben Finney


12