On Thu, May 18, 2017 at 8:08 AM, justin walters <walters.justi...@gmail.com> wrote:
> > > On Thu, May 18, 2017 at 12:09 AM, Deborah Swanson < > pyt...@deborahswanson.net> wrote: > >> Michael Torrie wrote, on Wednesday, May 17, 2017 3:11 PM >> > >> > On 05/17/2017 02:31 PM, Ned Batchelder wrote: >> > > Can you give an example of such a method? Often, that signature is >> > > used because there is no pre-conception of what the arguments might >> > > be. >> > >> > I'm not sure if this afflicts the standard library, but in my >> > own code, since Python doesn't support constructors with >> > different signatures, you pretty much have to rely on kwargs >> > with __init__() to handle different permutations of >> > construction arguments. Not that we don't know what the >> > arguments might be, we just don't know which of them we'll >> > have to deal with. Maybe the standard library is better >> > designed than my own code, but I find I have to implement >> > __init__(self, **kwargs) all the time. >> >> >> While I can respect and appreciate your specialized interest in specific >> kwargs, and I read in other's posts that help()'s display of kwargs is >> conditional on several things, I really want to reiterate and clarify my >> beginner's request. >> >> I know of several functions I've used without any *args or **kwargs >> because I had no clue from the docs what they might be. And, I know of >> one function now, from posts in this list, which takes kwargs, but I >> only know one application of one possible kwarg in this function because >> one member of this list, Peter Otten, used it in a suggestion he gave >> me. >> >> This is all there is in the docs for that function: >> >> >> somenamedtuple._replace(kwargs) >> >> Return a new instance of the named tuple replacing specified fields >> with new values: >> (Examples >> box)---------------------------------------------------------------| >> | >>> >> | >> | >> | >> | >>> p = Point(x=11, y=22) >> | >> | >>> p._replace(x=33) >> | >> | Point(x=33, y=22) >> | >> | >> | >> | >>> for partnum, record in inventory.items(): >> | >> | ... inventory[partnum] = >> record._replace(price=newprices[partnum], | >> | timestamp=time.now()) >> | >> |----------------------------------------------------------------------- >> ----| >> >> From this doc entry's example, I (sort of) get that x is a keyword arg >> that is to be equal to 33 for any use of p._replace() in the code >> following, until >> p._replace(kwarg) is respecified, or the code ends. So the response from >> Python shows me that p has transformed to the Point(x=33, y=22). A >> trivial example that doesn't begin to hint at the poential powers of >> this kwarg. >> >> The inventory[partnum] example is also quite trivial compared to the >> kwarg use that Peter showed me. >> >> When I asked him for an explanation, this is what I and he he said: >> >> Deborah Swanson wrote: >> >> > I know it's your "ugly" answer, but can I ask what the '**' in >> > >> > fix = {label: max(values, key=len)} >> > group[:] = [record._replace(**fix) for record in group] >> > >> > means? >> >> (Peter wrote) >> d = {"a": 1, "b": 2} >> f(**d) >> >> is equivalent to >> >> f(a=1, b=2) >> >> so ** is a means to call a function with keyword arguments when you want >> to >> decide about the *names* at runtime. Example: >> >> >>> def f(a=1, b=2): >> ... print("a =", a) >> ... print("b =", b) >> ... print() >> ... >> >>> for d in [{"a": 10}, {"b": 42}, {"a": 100, "b": 200}]: >> ... f(**d) >> ... >> a = 10 >> b = 2 >> >> a = 1 >> b = 42 >> >> a = 100 >> b = 200 >> >> Starting from a namedtuple `record` >> >> record._replace(Location="elswhere") >> >> creates a new namedtuple with the Location attribute changed to >> "elsewhere", >> and the slice [:] on the left causes all items in the `groups` list to >> be >> replaced with new namedtuples, >> >> group[:] = [record._replace(Location="elsewhere") for record in group] >> >> is basically the same as >> >> tmp = group.copy() >> group.clear() >> for record in tmp: >> group.append(record_replace(Location="elsewhere")) >> >> To support not just Location, but also Kind and Notes we need the double >> >> asterisk. >> >> >> >> Now, I understood what he meant from the context of the problem he was >> helping me with, but, how in the world would I have gotten that possible >> use of somenamedtuple._replace(kwargs) from the blurb and examples in >> the docs? Or any other possible kwarg and how they're used, including >> special operators like "**" ? >> >> If there's a tutorial somewhere on kwargs in general, what specific >> types of kwargs there are, what special operators can be specifed for >> them and how the kwargs are used, it would be very helpful to have that >> tutorial referenced in a footnote-link next to kwargs whenever kwargs is >> part of a function specification. >> >> Or, if no such tutorial exists, the footnote-link could point to any >> more detailed information that might explain what this creature "kwargs" >> might be. >> >> I have an item on my todo list to google "python kwargs", but it keeps >> getting pushed deeper and deeper down my list because I have so little >> hope that it will turn up anything useful. >> >> Deborah >> >> -- >> https://mail.python.org/mailman/listinfo/python-list >> > > > I agree that in a lot of situations `**kwargs` can be confusing. > > I usually operate under the assumption that a Python function takes two > kinds of values: > > `*args`: A (named)? tuple of values. For example: > > ``` > def foo(*args): > for arg in args: > print(arg) > > foo(1, 2 ,3) > >>> 1 > >>> 2 > >>> 3 > ``` > > However, when naming position arguments: > > ``` > def foo(a, b, *args): > for arg in args: > print(arg) > > foo(1, 2, 3) > >>> 3 > ``` > > Therefore, I find that `*args* can generally be considered as an index in > a (named)? tuple of > function arguments. > > `**kwargs`: An unpacked dictionary. Shares most of it's behavior with > `*args` except that > values must have keys. > > I find `**kwargs` to be especially useful working with Django's ORM: > > Suppose we have the following model class: > ``` > class Thing(models.Model): > name = models.CharField(max_length=255) > is_thing = models.BooleanField(blank=True, default=True) > ``` > > And we want to create a new instance of it from some json data our server > received > from the client: > ``` > data = json.loads(str(request.body, encoding="utf8")) > new_thing = Thing(**data).save() > ``` > > As long as the keys in the dictionary match the argument names for the > method, they will be passed > to the function as keyword arguments. Any keys that don't match argument > names and aren't explicitly > pulled of of the dict do not get used. in the above case, Django's Model > class does some behind > the scenes work with `hasattr` and `setattr` because it doesn't know until > runtime which Model has > which attributes. > > A simple example would be this class: > > ``` > class Bar(object): > > def __init__(self, **kwargs): > for key, value in kwargs.items(): > if hasattr(self, key): > setattr(self, key, value) > ``` > > We can then inherit from this class and create our own with some > attributes: > ``` > class MyBar(Bar): > > foo = "" > baz = "" > ``` > > We can populate an instance of our class with the following expressions: > ``` > data = {"foo": "hello", "baz": "world"} > my_instance = MyBar(**data) > > my_instance.foo > >>> "hello" > > my_instance.baz > >>> "world" > ``` > > So, args can be treated as a simple (named)? tuple or a simple dictionary. > `*` unpacks a list or tuple > and `**` unpacks a dictionary. I'm sure it's a lot more nuanced than this, > but for practical reasons, the > above explanation has never failed me. > > I hope I didn't miss the entire point of the thread. Someone needed help > with `**kwargs` right? > Made a mistake at the end there, meant to type: "So, args can be treated as a simple (named)? tuple or kwargs can be used as a simple dictionary." -- https://mail.python.org/mailman/listinfo/python-list