Live data from Hacker News

Notset: A Do-Not-Care value for Python

github.com

21–30 of 41 posts

Re: Notset: A Do-Not-Care value for Python

#21
post #19

I'm impressed by how much effort one can put into something that is effectively nothing else than: NotSet = type("NotSet", (object, ), dict(__repr__=lambda self: "NotSet")) There is no amazing concept, no new idea or even anything remotely interesting at all here. This is but a side-effect of a type system that encourages the usage of None in this way and the fact that None is generally used to mean undefined in keyw…

The implementation here is just a suggestion, and trust me, it wasn't much effort ;-)

The aspect that interests me is that this is a problem that crops up occasionally in different Python projects and there doesn't seem to be a recognized best-practice for addressing it.

(The `kwargs` approach seems like the most common but has all the downsides I mentioned.)

Re: Notset: A Do-Not-Care value for Python

#22
The kwargs form is a more obvious API. When your users grow more fields -- email, registration date, avatar -- this code will be reusable, whereas a function with a signature will need its arguments changed whenever the database schema changes.

The three-valued logic of set-to-something, set-to-None, don't-set is perfectly adequately captured by a dictionary. You're introducing an application-specific concept (the NotSet value) when a built-in concept (a dictionary) works just fine.

Re: Notset: A Do-Not-Care value for Python

#23
post #22

The kwargs form is a more obvious API. When your users grow more fields -- email, registration date, avatar -- this code will be reusable, whereas a function with a signature will need its arguments changed whenever the database schema changes. The three-valued logic of set-to-something, set-to-None, don't-set is perfectly adequately captured by a dictionary. You're introducing an application-specific concept (the No…

> whereas a function with a signature will need its arguments changed whenever the database schema changes.

True there is some work in keeping the two synchronized, but there are benefits.

First, unexpected arguments are immediately caught since they throw a TypeError.

Without this, you either have to manually check for unexpected keys (probably doing a set difference with `allowed_keys` or something) or you just silently pass through unrecognized attributes, probably causing strange behavior later on.

Second, you are forced to say explicitly which attributes are modifiable. To draw from the 'person' example, `name` and `age` might be modifiable, but `admin` might be protected. That would be made abundantly clear by `update(person, name=NotSet, age=NotSet)`, but less so, by `update(person, attrs)` or `update(person, kwargs)`.

A clear docstring would help, but I'd prefer to have the code just fail-fast on this unexpected input.

Re: Notset: A Do-Not-Care value for Python

#24
This is one of the features of Scala I like; explicit types for Option/Some/None in the core language and standard API. It's generally used for return types (ex: a hashmap 'get' will have a return type of Option[ValueType] and return either Some[ValueType] or None) but you can use it for function parameters as well.

  scala> def foo(name:Option[String] = None, age:Option[Int] = None) = {
       |     println("==========")
       |     if( name.isDefined )
       |         println("Name: " + name.get)
       |     if( age.isDefined )
       |         println("Age: " + age.get)
       |     println("==========")
       | }
  foo: (name: Option[String], age: Option[Int])Unit
  
  scala> foo()
  ==========
  ==========

  scala> foo(Some("Alice"))
  ==========
  Name: Alice
  ==========
  
  scala> foo(age = Some(10))
  ==========
  Age: 10
  ==========
Plus with some implicit syntactic sugar...

  scala> implicit def strToSome(s:String) = Some(s)
  strToSome: (s: String)Some[String]

  scala> implicit def intToSome(i:Int) = Some(i)
  intToSome: (i: Int)Some[Int]
 
  scala> foo("Alice")
  ==========
  Name: Alice
  ==========
  
  scala> foo(name = "Alice")
  ==========
  Name: Alice
  ==========
  
  scala> foo(age = 10)
  ==========
  Age: 10
  ==========
  
  scala> foo(name = "Alice", age = 10)
  ==========
  Name: Alice
  Age: 10
  ==========

Re: Notset: A Do-Not-Care value for Python

#25
My first impression is that this isn't a problem in need of solving; it just needs a change in approach.

The first is the conflation of classes and functions that work with classes. The update function in the example isn't reusable at all, implies you can update something other than a Person, and 'NotSet' doesn't fix that. So have it as a method on Person, and pass in a list of attributes to change as opposed to enumerating each field as a named parameter. You have the fields on the class for more fine-grained control, and functions like this don't necessarily make the code clearer.

Given that, I don't think the example presents a valid use-case for implementing 'NotSet' or whatever you want to call it. The problem is in the implementation, not Python, and the solution is a hack to enable you to continue with this approach.

Re: Notset: A Do-Not-Care value for Python

#26

The problem with this idea, or rather its implementation, is that it's just a matter of time until someone uses NotSet as a legitimate value assigned to a variable/attribute, just like None is today. At this point someone will introduce a new singleton, "NotSetIReallyMeanItThisTime", and so on and so forth. It never ends. The only way this might work is if NotSet (or whatever it's called) is a keyword and it is only…

> it's just a matter of time until someone uses NotSet as a legitimate value assigned to a variable/attribute, just like None is today.

No, because the NotSet value (unlike None!) isn't global; it's private to the package (or even class) that uses it. Callers never need to reference it, and never should.

(Python doesn't actually enforce access restrictions but using undocumented variables/attributes is frowned upon; if you do that, your code deserves to break!)

If you really want to hide it (to prevent mistakes), you could write something like this:

    NotSet = object()
    
    def isset(val, magic=NotSet):
        return val is magic
    
    def update(person, name=NotSet, age=NotSet):
        if isset(name):
            person.name = name
        if isset(age):
            person.age = age
    
    del NotSet
    
    update(None,name='foo')
    update(None,age=27)
(I'm sure there are still ways to get to the NotSet value if you really want to, but not by accident, and if you abuse this, you deserve all the problems you'll receive.)

Re: Notset: A Do-Not-Care value for Python

#28
post #27

Why not just use Ellipsis? That is a good "not set value". It is unique so others' Ellipsis is also your Ellipsis. It is already there, don't need to import anything, do any git pull or such.

Interesting. I love the idea of not having to import an external library, but much like the `NotImplementedError` suggestion, it overloads a value that already has a specific meaning, in this case related to slice-notation.

I'd worry that this approach could end up being even more confusing.

Re: Notset: A Do-Not-Care value for Python

#29
post #14

Without commenting on the suggestion itself, this really should be submitted as a PEP [1] and discussed in that context. [1] - http://www.python.org/dev/peps/

PEPs often begin life as simple emails (in the general form of the original post) to the python-ideas mailing list.

http://mail.python.org/mailman/listinfo/python-ideas

It's a list that's generally friendly to new ideas and you'll get some feedback from some old-and-crusty language designers. Which is neat.

Re: Notset: A Do-Not-Care value for Python

#30
post #12

Sorry for the bikeshedding, but double negatives are a real pain. Not so much for the code itself, but for talking to colleagues and giving talks. Off the top of my head, Empty would could work.

How about 'Omitted'? foo=Omitted if foo is not Omitted:

To my ear, this does sound the best. Thanks for the suggestion!
Post reply on HN