Ned Batchelder - Facts and Myths about Python names and values - PyCon 2015

Поділитися
Вставка
  • Опубліковано 18 жов 2024

КОМЕНТАРІ • 130

  • @sandysandeep7227
    @sandysandeep7227 8 років тому +249

    Came here from Automate the Boring Stuff with Python Video 16. :)

    • @huydong8774
      @huydong8774 6 років тому +3

      Same here. This is a really good material.

    • @AmeerulIslam
      @AmeerulIslam 6 років тому +3

      typed the link?

    • @alaminbijoy1027
      @alaminbijoy1027 6 років тому +1

      from IRC. someone refer me to this link when I asked a question question about name-value assignment.

    • @allmarketingtv
      @allmarketingtv 6 років тому +1

      Same!

    • @abdallahdataguy
      @abdallahdataguy 5 років тому +1

      same to me

  • @mayanksj
    @mayanksj 8 років тому +122

    My favourite quotes:
    1. Assignment never copies data.
    2. Python is neither "Call By value" nor "Call By Reference", it's "Call by Assignment"! Epic!
    3. There is no way in python where a name can refer to another name. A name can only refer to values. Oh my!

    • @domagojbosnjak75
      @domagojbosnjak75 4 роки тому +2

      Absolutely agree! Even though call by assignment comes down to the call by reference in C/C++

    • @bradywb98
      @bradywb98 3 роки тому

      C has ONLY pass by value. There is no pass by reference in C.

    • @skylerquinton1367
      @skylerquinton1367 3 роки тому

      instaBlaster.

    • @miraculixxs
      @miraculixxs Рік тому +1

      @@bradywb98 the fact that C is passing pointers by value is an implementation detail. It is still call by reference semantically.

    • @bradywb98
      @bradywb98 Рік тому

      @@miraculixxs The behavior that passed arguments exhibit is most certainly not an implementation detail.

  • @serpentphoenix
    @serpentphoenix 3 роки тому +3

    Oh man, he's probably the best Python teacher I've seen on UA-cam.

  • @hrmIwonder
    @hrmIwonder 2 роки тому +1

    I feel like that very last statement about a list as a default value will stick around forever & and potentially grow forever, is a rather important point.

  • @joecode5153
    @joecode5153 4 роки тому +5

    many...many... python tutorials later, something educational at last! :D
    It's one thing to say: "do it this way. now, do it your way" when in fact, "it" always does whatever you say, in a very particular way. Answered so many questions no tutorial ever teaches.
    Thanks Ned!

    • @monikaparmar2061
      @monikaparmar2061 3 роки тому +2

      That has unfortunately become the reality now. There are countless number of mediocre lectures/tutorials out there claiming to teach python. However very few do teach these internals of the language that help in understanding python in depth.

  • @aaron41
    @aaron41 Рік тому

    I've been programming in python for almost 10 years, and I still learned something new today!!!

  • @TimothyApe
    @TimothyApe 3 роки тому +1

    I am thankful for the pause button. I am able to follow, I just have to let my brain catch up with the speed of him talking.
    Great explanation none the less or for this very reason! :)

  • @dinethdewmina3530
    @dinethdewmina3530 Рік тому +1

    epic explanation completely shock my understanding of variables and lists

  • @markandrews5508
    @markandrews5508 9 років тому +2

    This was brilliant. It dealt with some of the subtleties at the heart of simple python statements and how these statements work differently to similar statements in other languages. Most people, myself included, don't quite get all of these subtleties until after many years of using python.

  • @UtahHeroes
    @UtahHeroes 9 років тому +17

    There is one more case that should be detailed here:
    my_lambdas = list()
    for x in range(10):
    my_lambdas.append(lambda y : y + x)
    my_lambdas[0](5) will return 14 and not 5
    To fix, you have to write it like this:
    my_lambdas = list()
    for x in range(10):
    my_lambdas.append(lambda y, x=x : y + x)
    my_lambdas[0](5) returns 5
    It's an esoteric case, but it's useful in frameworks such as Twisted.

    • @bharatgulati1903
      @bharatgulati1903 5 років тому +1

      this is an event listener problem, quite famous in js world. another approach is to use iife (aka immediately invoked function expression) in case one does not want to allow the x field to be tampered with in the lambda added to my_lambdas
      my_lambdas = list()
      for x in range(10):
      my_lambdas.append((lambda x: lambda y: y + x)(x))
      my_lambdas[0](5) # returns 5

    • @seandockery368
      @seandockery368 Рік тому

      Thank you for highlighting this, @UtahHeroes.
      It is actually a very salient point that names inside lambda suites aren't dereferenced until the lambda is executed... not when the lambda is assigned.
      I think that it is clearer to write it like this because it explicitly eliminates the confusion about an inner "x" shadowing the outer "x".
      my_lambdas = list()
      for x in range(10):
      my_lambdas.append(lambda y, z=x : y + z)

  • @PRS-0317
    @PRS-0317 Рік тому +1

    AHA! For the old asm/C programmers out there, the money shot is 20:15. Names/variables have scope, whereas all (allocated) values are on the heap and only 'leave scope' when the last reference to them leaves scope.

  • @hangugeohaksaeng
    @hangugeohaksaeng 9 років тому +1

    Great talk as always Mr. Batchelder. You are always succinct and articulate.

  • @rifatbhuiyan2543
    @rifatbhuiyan2543 3 роки тому

    15:45 "yeah that was that weird time that that list changed and I don't know why"
    Exactly that's why I'm here.

  • @domagojbosnjak75
    @domagojbosnjak75 4 роки тому +1

    23:18 question can have actually multiple answers. Depending on the types of the given elements list and the type of the modification the guest was talking about. So we could have this scenario:
    a_list = [1, [2, ], 4]
    x = a_list[1]
    a_list[1].append(3)
    print(x) # prints out modified version of the old value! List are mutable and the change happened in place!
    In contrast to this scenario:
    a_list = [1, 'ned', 4]
    x = a_list[1]
    a_list[1] += ' is awesome!'
    print(x) # prints out the old value. (The address x is pointing to actually never changed in both scenarios)
    print(a_list[1]) # prints 'ned is awesome' and of course now is: id(x) != id(a_list[1]) cause strings are immutable.
    P.S. Great stuff, thanks for the super instructive facts!

  • @thisrandomdude_
    @thisrandomdude_ 2 місяці тому

    this is so cool. informative and an absolute breeze to follow. tysm

  • @work_account_
    @work_account_ Рік тому +1

    what an awesome talk!

  • @das6109
    @das6109 2 роки тому

    Oddly enough the thing I wanted to understand most was addressed as the last question in the talk. Thanks to whoever asked it haha

  • @fidelpalma5164
    @fidelpalma5164 2 роки тому

    Came here thanks to Al Sweigart and Automate Boring Stuff with Python. Thanks Al, thanks Ned.

  • @Glicerol
    @Glicerol 5 років тому +9

    20:00 Names have no type & Values have no scope - really cool "duality" :)

  • @movax20h
    @movax20h 5 років тому +2

    I always recommend Python as a first programing language to others, but sometimes especially to people really far off CS background or with some mindset, really do have trouble with name passing, especially to functions. Just like this a_list = a_list + [val, var]. It is simply hard to understand and lengthy to explain to a person with zero previous coding or programming background. The cases like x += y, and special iadd for arrays, or apparent mutability of integers (x = 7, x += 3), makes it simply confusing.
    The big problem might be our attachment to saying "variables" for these thingies (x, a_list, etc). Where in fact they are not variables. We also often say "variable of type x", "this variable has this type". Where this is technically not true. Are "variables" are of the same type, they point (refer) to values. These values can have different types. The easy way of seeing that is that x = y, does exactly the same thing no matter what is the value (and type) of what y refers to, and no ability to overload equal sign operator (it is not an operator).
    I am starting to think that functional programming might be a better first programming language for many, because it basically forces you to do return a_list at the end by design (and do not allow you to do a_list = a_list ... anyway). In functional languages (Erlang, Haskell, Ocaml, Elixir, etc) you do not need to worry if you pass data to function by value or by reference or by name, and whatever it is copied or not, because in all these cases the result is the same. We usually call it "by value and copy", but really in implementation it is passed by reference (pointer). There is no difference because you can't mutate data anyway.

    • @PRS-0317
      @PRS-0317 Рік тому

      Not just zero previous coding experience - Python treats basic concepts like stack/heap variables differently in its syntax than every other programming language, and reuses established terms of art to mean different things that only apply to python. At least it's internally consistent? No wait - it's not. Some types are mutable and some aren't, but using the same operator (=) on them will yield fundamentally different behaviors (ptr/name reassignment vs memory allocation and then reassignment). Well, as a native speaker of English, I really can't complain about arbitrary, context dependent language rules.
      "I before E except after C, unless its a weird neighbor"

  • @Whatthetrash
    @Whatthetrash Рік тому

    Excellent talk! Thank you! :)

  • @jonassteinberg3779
    @jonassteinberg3779 4 роки тому +1

    ned is the man. find him on #python on irc. dude is always down to help with whatever. he's like the world's free python tutor, it's really quite remarkable.

  • @MagnusAnand
    @MagnusAnand 2 роки тому

    Amazing talk

  • @kusharora1435
    @kusharora1435 2 роки тому

    wow.. fantastic stuff. am so glad i saw this

  • @serhiy2020
    @serhiy2020 4 роки тому +1

    To illustrate that last point, this is what happens when you write a function like this:
    def func(nums = [1, 2]):
    nums[0] += 1
    print(nums)
    >>> func()
    [2, 2]
    >>> func()
    [3, 2]
    >>> func()
    [4, 2]
    ..and so on

    • @arthurpenndragon6434
      @arthurpenndragon6434 4 роки тому

      This example does a great job at making the theory part understandable in practice, thanks!

    • @1TW1-m5i
      @1TW1-m5i 3 роки тому

      If I'm understanding correctly, it's Becuase nums is only made when the function is being defined, but the change to it is made every time the function is called?

  • @joseville
    @joseville 3 роки тому

    Great talk. I learned many of these the hard way, but seeing it all summarized with clear succinct rules is great.
    8:15, wouldn't assignment have to copy data in that example?
    Call by assignment makes so much sense!

    • @ivolol
      @ivolol 3 роки тому +1

      What happened is the `+` on the right hand side copied the data (under the hood its like "hello".__add__(" there") returned a new value) and then the = just referred to that new data. But the = by itself never copied any data. It just referred to the new value that ended up on the RHS.

    • @joseville
      @joseville 3 роки тому

      @@ivolol thanks, ok that makes sense. The '=' itself didn't copy the data, it was the '+'.

  • @edchelstephens
    @edchelstephens 3 роки тому

    Amazing talk!

  • @timzhang6253
    @timzhang6253 2 роки тому

    great talk!

  • @Naz-yi9bs
    @Naz-yi9bs 3 роки тому

    Amazing, thank you!

  • @easy2useinsurance79
    @easy2useinsurance79 3 роки тому +1

    At 9:34 He says nums.append(7) does not make a new object, but x=x+1 makes a new object. But nums.append(7) does change the list to have a extra value at the end right?

    • @Am_Ende
      @Am_Ende 3 роки тому

      Yes, append modifies the list in place.

  • @gametimewitharyan6665
    @gametimewitharyan6665 Рік тому

    Came here from "Beyond the basic stuff with python" :)

  • @thomasgandalf4111
    @thomasgandalf4111 9 років тому +1

    great pres, thanks. just my 5c there to towards the end, "python is call by assignment". that's really a non-statement because in principle both values and references can be assigned. that said, python is call by reference as can easily be seen by the nice drawings on the slides - all names are references to values (objects, in fact). function calls assign (or bind) these references to names in the local scope. that is the very definition of call by reference. (it kind of puzzles me why people get confused by this -- there is really no difference to e.g. how pointers work in C, or how objects are passed in Java)

    • @drygordspellweaver8761
      @drygordspellweaver8761 2 роки тому

      Passing a value by reference and modifying said value does not always leave you with a modified value in Python, unlike in C++ where it always leaves you with a modified value. Hence it’s both/neither.

    • @miraculixxs
      @miraculixxs Рік тому

      @@drygordspellweaver8761 yes it does allow modification as long as the object is mutable. Mutable objects include e.g dict, list. It does not include str, int, float or any other scalar type. Regardless what gets passed on to a function is a reference to the object, never its value. That's the text book definition of call by reference.
      Mutablity of objects is a different trait entirely. It has no relation to and no impact in the nature of passing values on function calls.

    • @drygordspellweaver8761
      @drygordspellweaver8761 Рік тому

      @@miraculixxs I never said "it does not allow modification". I said it does not ALWAYS leave you with a modified value.
      "Regardless what gets passed on to a function is a reference to the object"
      ... Everything is technically an object in Python. Did you even watch the video where he is explaining the specific implementation of a value?
      "Mutablity of objects is a different trait entirely. It has no relation to and no impact in the nature of passing values on function calls."
      Okay- who or what are you even arguing against at this point? Your replies have nothing to do with the context of OP's conjecture and my response.

    • @miraculixxs
      @miraculixxs Рік тому

      @@drygordspellweaver8761
      Well yes, mutability is the key concept you seem to be confusing when you say Python does not always change the received object. The reason it does not in these cases is because the object is not mutable (e.g. a string), hence it cannot be modified. It is still passed by reference though.
      Call by reference and mutability are different concepts, we should not confuse them. Call by reference means passing, well, a reference to something. That's what Python does, always. Modifying an object requires for it to be mutable, regardless of how it is passed. That's a property of the object, not the calling style.
      You seem to imply that Python's way of variable passing in calling a function is somehow distinct from "by reference" *because* the received values are not always modifiable. That would be an inacurate conclusion - as noted above.
      Call by value oth means that a copy of a variable's value is created (typically by pushing the value to a stack, where the called function pops it off from). Python never does that.
      Actually there is an exception: in a multiprocessing setup, passing an object to a target function follows in fact call by value semantics, in that the value gets copied and reinstantiated in the receiving process. The actual function call again receives a reference though.

    • @drygordspellweaver8761
      @drygordspellweaver8761 Рік тому

      ​@@miraculixxs I think your confusion here stems from misinterpreting my statement "Hence it’s both/neither." I'm referring to both/neither "call by assignment" and "call by reference", not "call by value/call by reference" as you seem to think.

  • @pranjalmittal
    @pranjalmittal 9 років тому +1

    Excellent talk. Thank you. One question, in reference to 19:25, where you suggest to make a new list in function body.. Why not just pass the function a tuple instead of worrying about not modifying list arguments. I would assume it is better to correct things at the data type level (rather than taking care every time variable of that data type is used)?

    • @aaronhall8039
      @aaronhall8039 9 років тому +1

      Pranjal Mittal Your suggestion is a perfectly legitimate approach, and that's what I would recommend if you're just replacing things. However, I think Ned is talking about giving advice over IRC, in which case you just want a quick solution so the asker thanks you and goes away instead of dragging the whole channel into a discussion of mutability.

    • @nirajraut9408
      @nirajraut9408 4 роки тому +1

      @@aaronhall8039 Hey, at 18:34, there is a little reference created on the frame object while executing _return a_list._ If the memory of the frame object created for the function _append_twice_good_ is reclaimed, why isn't the memory of the "little" reference reclaimed even if it is present inside the frame object? In other words, how is it that the memory of the "little" reference not reclaimed and it can hold the reference if it's present inside the frame object and the memory of the frame object is reclaimed?

    • @aaronhall8039
      @aaronhall8039 4 роки тому

      @@nirajraut9408 That's an interesting question - I'm not quite clear on what you're asking, though. If you mean "val" isn't reclaimed, that's because Python uses reference counting and there's a reference in the outer scope to the object val points to. Similarly, a_list isn't reclaimed because now there's a reference to it in the outer scope as well. If objects that you think should be reclaimed aren't, then there must be a reference to it somewhere (perhaps a reference you created to try to track it?) Another point, ints from -5 to 256 are interned so they are always available and aren't duplicated in memory. Finally, don't worry so much about tracking memory in Python unless you've got a memory leak and are running out of it...

  • @Fire_Rice
    @Fire_Rice 4 роки тому +1

    Lol I was dealing with the list problem today. I thought I found a glitch or something xD

  • @michaelstreeter3125
    @michaelstreeter3125 3 роки тому +1

    Remember: *"Names have no type; values have no scope"* - is this the genius of Python? I'm going to have to go away and think now.

    • @drygordspellweaver8761
      @drygordspellweaver8761 2 роки тому

      “zip turns a pair of streams into a stream of pairs”
      Ned is brilliant

  • @BayesianHorse
    @BayesianHorse 9 років тому

    I think at 11:30 the problem of understanding the docs is that the __iadd__ operator should return the result of the operation(docs.python.org/2/reference/datamodel.html#object.__iadd__), which may or may not be the same object. The particular detail of list's += behavior is not mentioned, but "extend" is supposedly the equivalent of a[len(a):] = [x], which is a range assignment and thus modifies the data. It's unfortunately not equivalent to a = a + [x], which is unfortunate I think.

  • @TommyCarstensen
    @TommyCarstensen 9 років тому +3

    I would like to drink a beer with Ned :) He seems friendly and knowledgeable :)

  • @saurabh75prakash
    @saurabh75prakash 5 років тому

    Must watch for a Pythonista.

  • @luckyboy20021
    @luckyboy20021 6 років тому

    Great video, it help me a lot

  • @scottcampbell2707
    @scottcampbell2707 5 років тому

    I found this video very useful.

  • @solamarpreet
    @solamarpreet 2 роки тому

    Ty ned

  • @bethfiore
    @bethfiore 4 місяці тому

    How did he figure this all out!?

  • @amnanajib8167
    @amnanajib8167 2 роки тому +2

    For anyone having a confusion between what is told in 11:11 and 18:26:
    Actually the __isadd__ method is run under the hood of x += y and and not for x = x + y. So, if you reproduce the example under 18:28 with a_list += [val, val ], it should work fine, since then you are just mutating the val of a_list.

  • @aaronhall8039
    @aaronhall8039 9 років тому +6

    I've got my Python gold badge on StackOverflow, and I watched this talk from beginning to end, and I have no nitpicks to make over it. Good job, Ned.

    • @ChrisLaffra
      @ChrisLaffra 9 років тому +1

      At slide 18, at 13:40 into the talk, Ned is avoiding the use of an explicit iterator (probably due to time limitations). However, I still would have been more formal, not use sequence[...], and instead say the for loop is the equivalent of:
      it = iter(sequence)
      while True:
      try:
      x = it.next()
      except StopIteration:
      break
      something(x)

    • @aaronhall8039
      @aaronhall8039 9 років тому +2

      Chris Laffra Sure, that's correct. I think his level of sophistication was fine, though your example makes it even more clear without the hand-waving. To further refine your example I would call next(it) instead of it.next().

    • @drygordspellweaver8761
      @drygordspellweaver8761 2 роки тому

      You are the student, not the teacher. Humble yourself.

  • @Lolwutdesu9000
    @Lolwutdesu9000 6 років тому

    At 22:32, the guy talks about the two versions of the 2D list code. I just tried them out, and the "bad" one runs about 5 times faster. Why on Earth is it the "bad" one?!

    • @henryzhang2992
      @henryzhang2992 6 років тому +4

      Because then I believe that you then create references to the same list, which would mean any modification would affect all the rows instead of just that one

  • @patriotir
    @patriotir 7 місяців тому

    19:28 he recommends to create a new list every time but isn't that counter-intuitive? creating a new list means consuming more memory space while we can easily mutate the list. to me the first function is more efficient.

  • @gustavonobregadossantos1223
    @gustavonobregadossantos1223 4 роки тому

    I came here because of "Automate Boring Stuff with Python"! If this is true for somebody else, I have a question. What did you do next, after "Automate Boring stuff with Python"?

    • @drygordspellweaver8761
      @drygordspellweaver8761 2 роки тому

      I just started writing scripts and looked into software design principles

  • @_elkd
    @_elkd Рік тому

    For the 2D list in the last slide, here is Amy's talk section covering that ua-cam.com/video/sH4XF6pKKmk/v-deo.html

  • @vwwvwwwvwwwvwwvvwwvw
    @vwwvwwwvwwwvwwvvwwvw 6 років тому

    What is the answer to the puzzle? Start->8549176320->Null ? Significance?

    • @einSteppenwolf
      @einSteppenwolf 6 років тому +2

      The digits are in the alphabetical order of their English names.

  • @RonJohn63
    @RonJohn63 7 років тому

    23:56 IOW, no pointers?

  • @tbabbittt
    @tbabbittt 8 років тому

    Python is a class oriented programming language.When you do something like x = 23 x becomes a integer class complete with all the properties of the integer class. x never refers to a value, a name refers to a class instance that lies in the namespace and the data that lies within that instance. I think that when he says value he actually means class instance or he is not talking about Python at all.

    • @tbabbittt
      @tbabbittt 8 років тому

      Remember that x=25 and y=25 create two seperate instances of the integer class in namespace. x and y are not referring to the save value but to different class instances. When you say x=y that says that y now also refers to the class instance in namespace (the dir() function lists namespaces). Now x can affect the properties and methods (no values here) of the class instance in the namespace.

    • @iffinity7704
      @iffinity7704 8 років тому +4

      > Remember that x=25 and y=25 create two separate instances of the integer class
      Not entirely true. For some range of values, it's the same instance
      Python 3.5.2
      >>> x = 25
      >>> y = 25
      >>> x is y
      True
      >>> x = 123456789
      >>> y = 123456789
      >>> x is y
      False
      And it's logical, it will be a big overhead to create new int object every time you need 0 or 1 or 10.
      IIRC, strings for up to length 20 behave the same way.

    • @drygordspellweaver8761
      @drygordspellweaver8761 2 роки тому

      Ints from -5 to 256 are always interned so they won’t duplicate memory use.

  • @movax20h
    @movax20h 5 років тому

    a_list += [2, 3], that it iadd returns self, is generic guidline of iadd operator:
    object.__iadd__(self, other)
    "[...] These methods should attempt to do the operation in-place (modifying self) and return the result (which could be, but does not have to be, self). [...]"
    The fcat that x = 7, x += 3; produces new value 10, is because type int do not have iadd (in-place add) at all. Python tries x = x.__iadd__(3), but because __iadd__ do not exist, it does x = x.__add__(3) (which will success), or if that doesn't exist either, will try x = (3).__radd__(x).
    So the x += 3, is not special at all for int vs arrays. It is because x += y, is not equal to x = x + y in general at all (they can both make x completely different values at the same time, and make x refer to different or the same things at the end at will).

  • @pwbpeter
    @pwbpeter 9 років тому +1

    I thought this was about names in Monty python Mr ftang ftang ole biscuit barrel and such like ,Imagine my dissapointment!

  • @paraglide01
    @paraglide01 6 років тому +3

    Came here from kittens meet puppy's first time.

  • @aberezh
    @aberezh 6 років тому

    I am still not following how this is different from e.g. Java. In Java it is clearly "pass by value", just some values happen to be references. Well, in Python all values are references, still in the original meaning of the terms it is "pass by value".

    • @aberezh
      @aberezh 6 років тому

      I other words, once you speak about variables, they are all references. Once you speak about names, they are "bound to values". So, as I see it now, the talk does not deny the existence of variables, just suggests an alternative way to look at what happens in Python.

  • @CristiNeagu
    @CristiNeagu 7 років тому

    I would say Python definitely uses call by reference, but it deals with references in a different manner than C/C++. So while in C/C++ you know that passing a value by reference and doing something to it will always leave you with a modified value at the end, in Python that is not always the case.
    But it's definitely 100% call by reference.

  •  9 років тому +1

    "Python has no variables" is not a silly way to explain "variables" at all, Ned.

    • @nedbatchelder
      @nedbatchelder 9 років тому +6

      Jürgen Erhard We'll have to agree to disagree.

    • @nirajraut9408
      @nirajraut9408 4 роки тому

      @@nedbatchelder Hey, at 18:34, there is a little reference created on the frame object while executing _return a_list._ If the memory of the frame object created for the function _append_twice_good_ is reclaimed, why isn't the memory of the "little" reference reclaimed even if it is present inside the frame object? In other words, how is it that the memory of the "little" reference not reclaimed and it can hold the reference if it's present inside the frame object and the memory of the frame object is reclaimed?

  • @shivampandiya
    @shivampandiya 3 роки тому

    legend.py

  • @andysondur
    @andysondur 4 роки тому

    19:10 doesn't work. Just adding return statement to the def function doesn't change the reference (nums still refer to the original nums list). The 'best advice' doesn't seem to be best advice after all. The append command is the best way to modify a list inplace.

    • @m1k1a1
      @m1k1a1 4 роки тому +2

      But he didn't *just* add the return statement. The previous slide showed how he used the returned value: nums = append_twice_good(nums, 7) . That re-binds nums to the new list. As someone who used other languages before Python, this is the way I wrote my first function to modify a list.

    • @JethroYSCao
      @JethroYSCao 3 роки тому

      Reread his code. He didn't only put "return" as the last line of the append_twice_good function, he put "return a_list"

    • @andysondur
      @andysondur 3 роки тому

      @@m1k1a1 thanks (a year a later)

    • @andysondur
      @andysondur 3 роки тому +1

      @@JethroYSCao Yes, I realised my mistake. Thanks for the help.

  • @tonyravindran
    @tonyravindran 5 років тому +1

    Came here from Automate the Boring Stuff with Python Video 16

  • @anudeep13
    @anudeep13 3 роки тому

    Came here from WhiteHat Jr course. Yes, I am a 5 yr old.

  • @Alpacastan21m
    @Alpacastan21m 4 роки тому

    Automate the Boring Stuff.

  • @daronieogarniaro
    @daronieogarniaro 11 місяців тому

    AUTOMATE THE BORING STUFF

  • @lucashallo4760
    @lucashallo4760 4 роки тому

    AUTOMATE THE BORING STUFF