Re: some problems for an introductory python test
On 8/10/2021 5:27 PM, Hope Rouselle wrote: Terry Reedy writes: On 8/10/2021 9:15 AM, Hope Rouselle wrote: 2.__add__(3) SyntaxError: invalid syntax But then I tried: (2).__add__(3) 5 Add a space is easier. 2 .__add__(3) 5 Hah. That's brilliant! So cool. Python is a little looser about whitespace than one might expect from reading 'normal' code when the result is unambiguous in that it cannot really mean anything other than what it does. Two other examples: >>> if3: print('yes!') yes! >>> [0] [0] 0 -- Terry Jan Reedy -- https://mail.python.org/mailman/listinfo/python-list
Re: on slices, negative indices, which are the equivalent procedures?
Apologies for lateness. Coincidentally, I've been asked to speak to our local Python Users' Group on slicing. Herewith an attempt to modify those demos around your data/question. Apologies if the result is thus somewhat lacking in flow. Also, whereas I prefer to illustrate 'how it works', I perceive that you are used to learning 'rules' and only thereafter their application (the teaching-practice under which most of us learned) - so, another reason for mixing-things-up, to suit yourself (hopefully). TLDR; if you are a Python-Master there's unlikely to be anything here for you. However, if you have the time, I would appreciate any and all suggestions for improvement and correction. Please feel free to send off-list, should you prefer... As mentioned, the base-material (with different examples) is for a talk. However, I should reserve the right to turn it into an article (in my spare-time - a concept which could spawn a lengthy list-discussion on its own!). On 06/08/2021 05.35, Jack Brandom wrote: > The FAQ at > > https://docs.python.org/3/faq/programming.html#what-s-a-negative-index > > makes me think that I can always replace negative indices with positive > ones --- even in slices, although the FAQ seems not to say anything > about slices. Yes, it can be done. There's (much, much) more to the docs than the FAQ! > With slices, it doesn't seem to always work. For instance, I can > reverse a "Jack" this way: > s = "Jack Brandom" s[3 : -13 : -1] > 'kcaJ' > > I have no idea how to replace that -13 with a positive index. Is it > possible at all? Yes it is - but read on, gentle reader... If we envisage a string: - a positive index enables the identification of characters, from left-to-right - a negative index 'counts' from right-to-left, ie it takes the right-most elements/characters. The length of a string (sequence) is defined as len( str ). Accordingly, there is a formula which can 'translate' the length and either statement of the index's relative-location, to the other. To quote one of the web.refs (offered below) "If i or j is negative, the index is relative to the end of sequence s: len(s) + i or len(s) + j is substituted. But note that -0 is still 0." Clear as mud? Please continue... > But this example gives me the idea that perhaps each slice is equivalent > to a certain loop (which I could write in a procedure). So I'm looking > for these procedures. If I can have the procedures in mind, I think I > will be able to undersand slices without getting surprised. > > Do you have these procedures from the top of your mind? While I haven't > given up yet, I am not getting too close. Thank you! Us Silver-Surfers have to stick-together. So, let's try closing the gap. Rather than attempting to re-build Python, perhaps some experimenting with sequences, indexing, and slicing is what is required? I can see writing a simulation routine as a perfectly-valid (proof of) learning-approach - and one we often applied 'back then'. However, is it the 'best' approach? (Only you can judge!) I would fear that the effort (and perhaps frustration) of attempting to code slicing manually may add coding-problems to the mix, and start to erect a 'brick wall' or a 'greasy slope', in grand Sisyphian tradition. In attempting to learn two things at the same time, are you gambling against the old advice about "never take (only) two chronometers to sea"? ie when things don't work, is the error in the code, in the implementation of slicing, or in one's understanding/learning of slicing??? Another, word-of-warning from an 'old hand': don't try to make Python look like some 'previous language'. Similarly, just because it looks similar doesn't mean Python will work the same way as xyz-language! Enough with the philosophy, diving right in:- "Subscription" is the process of selecting an item from a sequence. Thus, an index is a means of locating/evaluating a single item within a sequence, eg 0 locates the "J" within "Jack": >>> "Jack"[ 0 ] 'J' Python offers several "built-in" sequences: strings, lists, tuples, bytes, bytearrays, and ranges. May I take it that you have at least a basic understanding of the first three? - more about ranges later... Your examples have been strings, and that's a good application for (and illustration of) slices (aka "sub-strings"). So, we'll stick with them. However, the behaviors are replicated in other sequence-structures - albeit with perhaps fewer real-world applications. Python has other built-in collections. Sets do not index their elements, by definition; and although "mappings"/dicts do use a form of subscripting (which need not be numeric), they cannot be sliced. Similarly, namedtuples (which offer both forms of subscripting). Accordingly, they have been ignored for today's purposes. It is worth noting though, advanced-users coding an aggregate custom-class, can add indexing and slicing capabilities by coding a __getitem__() method. A Python "con
Re: some problems for an introductory python test
Terry Reedy writes: > On 8/10/2021 9:15 AM, Hope Rouselle wrote: > 2.__add__(3) >> SyntaxError: invalid syntax >> But then I tried: >> > (2).__add__(3) >> 5 > > Add a space is easier. 2 .__add__(3) > 5 Hah. That's brilliant! So cool. -- https://mail.python.org/mailman/listinfo/python-list
Re: some problems for an introductory python test
Chris Angelico writes: > On Wed, Aug 11, 2021 at 4:14 AM Hope Rouselle wrote: >> >> Chris Angelico writes: >> >> > On Tue, Aug 10, 2021 at 7:25 AM Hope Rouselle >> > wrote: >> >> I came up with the following question. Using strings of length 5 >> >> (always), write a procedure histogram(s) that consumes a string and >> >> produces a dictionary whose keys are each substrings (of the string) of >> >> length 1 and their corresponding values are the number of times each >> >> such substrings appear. For example, histogram("a") = {"a": 5}. >> >> Students can "loop through" the string by writing out s[0], s[1], s[2], >> >> s[3], s[4]. >> > >> > In other words, recreate collections.Counter? Seems decent, but you'll >> > need to decide whether you want them to use defaultdict, use >> > __missing__, or do it all manually. >> >> Yes, the course introduces very little so there is a lot of recreation >> going on. Hm, I don't know defaultdict and I don't know how to use >> __missing__. The course does introduce dict.get(), though. If students >> use dict.get(), then the procedure could essentially be: >> >> def histogram(s): >> d = {} >> d[s[0]] = d.get(s[0], 0) + 1 >> d[s[1]] = d.get(s[1], 0) + 1 >> d[s[2]] = d.get(s[2], 0) + 1 >> d[s[3]] = d.get(s[3], 0) + 1 >> d[s[4]] = d.get(s[4], 0) + 1 >> return d > > There's nothing wrong with getting students to recreate things, but > there are so many different levels on which you could do this, which > will leave your more advanced students wondering what's legal. :) Here > are several ways to do the same thing: [... very impressive set of solutions...] > It seems *very* strange to have an exercise like this without looping. It is. I agree. > That seems counterproductive. It is. > But if you're expecting them to not use loops, you'll want to also be > very clear about what other features they're allowed to use - or > alternatively, stipulate what they ARE allowed to use, eg "Use only > indexing and the get() method". Yes, I will do that. I mean the course does that all the time. They cannot use anything that has not been introduced. That's another problem because the course introduces various things and students can't quite keep everything in mind. The suggestion to make up a list of things is mostly ignored by nearly all of them. :-) -- https://mail.python.org/mailman/listinfo/python-list
Re: annotations cause dataclass fields type side effects
> On 9 Aug 2021, at 19:28, Lukas Lösche wrote: > > I'm on Python 3.9.6 and trying to make sense of the following behaviour: > from dataclasses import dataclass, fields @dataclass > ... class Foobar: > ... name: str > ... fields(Foobar)[0].type > type(fields(Foobar)[0].type) > > > > from __future__ import annotations from dataclasses import dataclass, fields @dataclass > ... class Foobar: > ... name: str > ... fields(Foobar)[0].type > 'str' type(fields(Foobar)[0].type) > > > I have a validation function that checks if the types of all fields in > a dataclass are what they are supposed to be. But as soon as I import > annotations from __future__ this validation breaks as the arg that I'm > passing to isinstance() is no longer a type class but a string. > > What am I doing wrong? Nothing. As I understand the current situation you have to check if it’s a string and convert to the object you need at the moment. Hopefully someone with a deeper understanding will explain how you do this. I would guess use eval. This is a known problem and there are core devs that are working on improvements. But it will be at leas python 3.11 before there is a possible improvement. Barry > > Thanks, > -- Lukas > -- > https://mail.python.org/mailman/listinfo/python-list > -- https://mail.python.org/mailman/listinfo/python-list
Re: some problems for an introductory python test
On 8/10/2021 9:15 AM, Hope Rouselle wrote: 2.__add__(3) SyntaxError: invalid syntax But then I tried: (2).__add__(3) 5 Add a space is easier. >>> 2 .__add__(3) 5 >>> -- Terry Jan Reedy -- https://mail.python.org/mailman/listinfo/python-list
Re: some problems for an introductory python test
On Wed, Aug 11, 2021 at 4:18 AM Hope Rouselle wrote: > > Chris Angelico writes: > > [...] > > >> not disagreeing... and yeah I could have thought deeper about the > >> answer, but I still think "notthing has been OOP" -> "yes it has, they > >> just didn't realize it" was worth mentioning > > > > Oh yes, absolutely agree. > > At the same time, inside the machine nothing is OOP --- so all the OOP > is no OOP at all and they just didn't realize it? This seems to show > that OOP is about perspective. An essential thing for OOP is the > keeping of states. Closures can keep state, so having procedures as > first-class values allows us to say we are doing OOP too. (Arguments of > procedures are messages and function application is message passing, > with closures keeping a state --- and all the rest of OOP can be > implemented with enough such functional technology.) In summary, OOP > should not be defined as some special syntax, otherwise there is no OOP > in ``2 + 2''. > > Having said that, I totally agree with all the nitpicking. Object orientation is a particular abstraction concept. It's not a feature of the machine, it's a feature of the language that you write your source code in. I've done OOP using IDL and CORBA, writing my code in C and able to subclass someone else's code that might have been written in some other language. [1] Central tenets of OOP (polymorphism, inheritance, etc) can be implemented at a lower level using whatever makes sense, but *at the level that you're writing*, they exist, and are useful. Data types, variables, control flow, these are all abstractions. But they're such useful abstractions that we prefer to think that way in our code. So, *to us*, those are features of our code. To the computer, of course, they're just text that gets processed into actually-executable code, but that's not a problem. So I would say that (in Python) there IS object orientation in "2 + 2", and even in the Python C API, there is object orientation, despite C not normally being considered an object-oriented language. ChrisA [1] And boy oh boy was that good fun. The OS/2 Presentation Manager had a wealth of power available. Good times, sad that's history now. -- https://mail.python.org/mailman/listinfo/python-list
Re: some problems for an introductory python test
On Wed, Aug 11, 2021 at 4:18 AM Hope Rouselle wrote: > > I totally agree with you but I didn't know that even numbers were like > that in Python. In fact, I still don't quite believe it... > > >>> 2.__add__(3) > SyntaxError: invalid syntax Yeah, that's because "2." looks like the beginning of a float. > But then I tried: > > >>> (2).__add__(3) > 5 > > Now I do believe it! :-) Awesome. I had no idea. You can also do it this way: >>> x = 2 >>> x.__add__(3) 5 But don't teach this; it ignores several problems. Notably, a subclass should be able to (re)define operators: >>> class FancyNumber(int): ... def __repr__(self): return "FancyNumber(%d)" % self ... def __add__(self, other): return type(self)(int(self) + other) ... def __radd__(self, other): return type(self)(other + int(self)) ... >>> FancyNumber(2) + 3 FancyNumber(5) >>> 2 + FancyNumber(3) FancyNumber(5) >>> FancyNumber(2).__add__(3) FancyNumber(5) >>> (2).__add__(FancyNumber(3)) 5 With the + operator, you always get a FancyNumber back. Explicitly calling the dunder method fails. (General principle: Dunder methods are for defining, not for calling, unless you're in the process of defining a dunder.) > (*) More opinions > > So, yeah, the idea of a course like that is to try to simplify the world > to students, but it totally backfires in my opinion. There is so much > syntax to learn, so many little details... We spend the entire semester > discussing these little details. Agreed, and if you try to teach all the syntax, you're inevitably going to get bogged down like that. > I posted here recently a study of the semantics of slices. When I > finally got it, I concluded it's not very simple. The course introduces > a few examples and expects students to get it all from these examples. > I would rather not introduce slices but teach students enough to write > procedures that give them the slices. The slices are the fish; learning > to write the procedures is the know-how. (I'm fine with even teaching > them how to write procedures to add or subtract numbers [and the rest of > arithmetic], although none of them would find mysterious what is the > meaning of arithmetic expressions such as 3 + 6 - 9, even taking > precedence of operators into account. That's syntax they already know. > If we teach them the syntax of procedures, we could be essentially done > with syntax.) A language like Python is useful, not because every tiny part of it is easy to explain, but because the language *as a whole* does what is expected of it. Toy languages are a lot easier to explain on a concrete level (look up Brainf*ck - it has very few operations and each one can be very simply defined), and that might be tempting in terms of "hey, we can teach the entire language in ten minutes", but they're utterly useless for students. Instead, my recommendation would be: Entice students with power. Pretend you're teaching some aspect of magic at a wizards' school and make sure your students feel massively OP. Sure, they might not understand what makes those cantrips work, but they know that they DO work. You can have students casting print calls left and right, doing complex incantations with list comprehensions, and even defining their own classes, all without ever worrying about the details of how it all works. Then once the "wow" is achieved, you can delve into the details of how things actually function, transforming progressively from "wizardry" to "science". And to be quite frank, a lot of code IS magic to most programmers. Which isn't a problem. Do you REALLY need to understand how your CPU does register renaming in order to benefit from it? Or do you need to know every detail of the TCP packet header before using the internet? Not likely. Those details hide away under the covers, and we make happy use of them. There's infinite knowledge out there, and you - and your students - are free to dip into exactly as much as you're comfortable with. ChrisA -- https://mail.python.org/mailman/listinfo/python-list
Re: some problems for an introductory python test
On Wed, Aug 11, 2021 at 4:14 AM Hope Rouselle wrote: > > Chris Angelico writes: > > > On Tue, Aug 10, 2021 at 7:25 AM Hope Rouselle > > wrote: > >> I came up with the following question. Using strings of length 5 > >> (always), write a procedure histogram(s) that consumes a string and > >> produces a dictionary whose keys are each substrings (of the string) of > >> length 1 and their corresponding values are the number of times each > >> such substrings appear. For example, histogram("a") = {"a": 5}. > >> Students can "loop through" the string by writing out s[0], s[1], s[2], > >> s[3], s[4]. > > > > In other words, recreate collections.Counter? Seems decent, but you'll > > need to decide whether you want them to use defaultdict, use > > __missing__, or do it all manually. > > Yes, the course introduces very little so there is a lot of recreation > going on. Hm, I don't know defaultdict and I don't know how to use > __missing__. The course does introduce dict.get(), though. If students > use dict.get(), then the procedure could essentially be: > > def histogram(s): > d = {} > d[s[0]] = d.get(s[0], 0) + 1 > d[s[1]] = d.get(s[1], 0) + 1 > d[s[2]] = d.get(s[2], 0) + 1 > d[s[3]] = d.get(s[3], 0) + 1 > d[s[4]] = d.get(s[4], 0) + 1 > return d There's nothing wrong with getting students to recreate things, but there are so many different levels on which you could do this, which will leave your more advanced students wondering what's legal. :) Here are several ways to do the same thing: >>> s = "hello world" >>> from collections import Counter; Counter(s) Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1}) >>> from collections import defaultdict >>> hist = defaultdict(int) >>> for ltr in s: hist[ltr] += 1 ... >>> hist defaultdict(, {'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1}) >>> class Incrementer(dict): ... def __missing__(self, key): return 0 ... >>> hist = Incrementer() >>> for ltr in s: hist[ltr] += 1 ... >>> hist {'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1} >>> hist = {} >>> for ltr in s: hist[ltr] = hist.get(ltr, 0) + 1 ... >>> hist {'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1} >>> hist = {} >>> for ltr in s: ... if ltr in hist: hist[ltr] += 1 ... else: hist[ltr] = 1 ... >>> hist {'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1} >>> hist = {} >>> for ltr in s: ... try: hist[ltr] += 1 ... except KeyError: hist[ltr] = 1 ... >>> hist {'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1} A Counter shows the values ranked, all the others retain insertion order, but they all get to the same result. It seems *very* strange to have an exercise like this without looping. That seems counterproductive. But if you're expecting them to not use loops, you'll want to also be very clear about what other features they're allowed to use - or alternatively, stipulate what they ARE allowed to use, eg "Use only indexing and the get() method". ChrisA -- https://mail.python.org/mailman/listinfo/python-list
Re: some problems for an introductory python test
Mats Wichmann writes: > On 8/9/21 3:07 PM, Hope Rouselle wrote: >> I'm looking for questions to put on a test for students who never had >> any experience with programming, but have learned to use Python's >> procedures, default arguments, if-else, strings, tuples, lists and >> dictionaries. (There's no OOP at all in this course. Students don't >> even write ls.append(...). They write list.append(ls, ...)). > > Nitpickery... there *is* OOP in the course, they just don't know it. > > Long long ago (over 20 yrs now) I developed a Python course for a > commercial training provider, and in it I claimed one of the great > things about Python was it supported all kinds of object oriented > programming techniques, but you could also use it without doing > anything object oriented. If I wrote a course now, I'd never make that > claim, because everything you do in Python is pretty much object > oriented. > x = list() type(x) > dir(x) > ['__add__', '__class__', '__class_getitem__', '__contains__', > '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', > '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', > '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', > '__iter__', '__le__', '__len__', ' > __lt__', '__mul__', '__ne__', '__new__', '__reduce__', > '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', > '__setattr__', '__setitem__', '__sizeof__', '__str__', > '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', > 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] > > list is a class and it has methods... it's "object-oriented"! > > > Even if you do > > x = 2 + 3 > > you're actually creating an integer object with a value of 2, and > calling its add method to add the integer object with the value of 3 > to it. The syntax hides it, but in a way it's just convenience that it > does so... > 2 + 3 > 5 x = 2 x.__add__(3) > 5 > > > sorry for nitpicking :) But... don't be afraid of letting them know > it's OOP, and it''s not huge and complex and scary! I totally agree with you but I didn't know that even numbers were like that in Python. In fact, I still don't quite believe it... >>> 2.__add__(3) SyntaxError: invalid syntax But then I tried: >>> (2).__add__(3) 5 Now I do believe it! :-) Awesome. I had no idea. (*) More opinions So, yeah, the idea of a course like that is to try to simplify the world to students, but it totally backfires in my opinion. There is so much syntax to learn, so many little details... We spend the entire semester discussing these little details. I posted here recently a study of the semantics of slices. When I finally got it, I concluded it's not very simple. The course introduces a few examples and expects students to get it all from these examples. I would rather not introduce slices but teach students enough to write procedures that give them the slices. The slices are the fish; learning to write the procedures is the know-how. (I'm fine with even teaching them how to write procedures to add or subtract numbers [and the rest of arithmetic], although none of them would find mysterious what is the meaning of arithmetic expressions such as 3 + 6 - 9, even taking precedence of operators into account. That's syntax they already know. If we teach them the syntax of procedures, we could be essentially done with syntax.) -- https://mail.python.org/mailman/listinfo/python-list
Re: some problems for an introductory python test
Chris Angelico writes: [...] >> not disagreeing... and yeah I could have thought deeper about the >> answer, but I still think "notthing has been OOP" -> "yes it has, they >> just didn't realize it" was worth mentioning > > Oh yes, absolutely agree. At the same time, inside the machine nothing is OOP --- so all the OOP is no OOP at all and they just didn't realize it? This seems to show that OOP is about perspective. An essential thing for OOP is the keeping of states. Closures can keep state, so having procedures as first-class values allows us to say we are doing OOP too. (Arguments of procedures are messages and function application is message passing, with closures keeping a state --- and all the rest of OOP can be implemented with enough such functional technology.) In summary, OOP should not be defined as some special syntax, otherwise there is no OOP in ``2 + 2''. Having said that, I totally agree with all the nitpicking. -- https://mail.python.org/mailman/listinfo/python-list
Re: some problems for an introductory python test
Chris Angelico writes: > On Tue, Aug 10, 2021 at 7:25 AM Hope Rouselle wrote: >> I came up with the following question. Using strings of length 5 >> (always), write a procedure histogram(s) that consumes a string and >> produces a dictionary whose keys are each substrings (of the string) of >> length 1 and their corresponding values are the number of times each >> such substrings appear. For example, histogram("a") = {"a": 5}. >> Students can "loop through" the string by writing out s[0], s[1], s[2], >> s[3], s[4]. > > In other words, recreate collections.Counter? Seems decent, but you'll > need to decide whether you want them to use defaultdict, use > __missing__, or do it all manually. Yes, the course introduces very little so there is a lot of recreation going on. Hm, I don't know defaultdict and I don't know how to use __missing__. The course does introduce dict.get(), though. If students use dict.get(), then the procedure could essentially be: def histogram(s): d = {} d[s[0]] = d.get(s[0], 0) + 1 d[s[1]] = d.get(s[1], 0) + 1 d[s[2]] = d.get(s[2], 0) + 1 d[s[3]] = d.get(s[3], 0) + 1 d[s[4]] = d.get(s[4], 0) + 1 return d >> I think you get the idea. I hope you can provide me with creativity. I >> have been looking at books, but every one I look at they introduce loops >> very quickly and off they go. Thank you! > > Probably because loops are kinda important? :) Totally important. But each course author thinks they know better. Sometimes a college professor can do very little to help her students. I am actually fond of functional programming as a first course using a language with as little syntax as possible. Python is very nice but it's not a small language. It's easy to see courses spending an entire semester on introducing syntax and this one is no different. I think it's more interesting to see all the syntax in a few minutes and spend the semester on strategies. -- https://mail.python.org/mailman/listinfo/python-list
Re: on slices, negative indices, which are the equivalent procedures?
Chris Angelico writes: > On Tue, Aug 10, 2021 at 7:24 AM Jack Brandom wrote: >> >> Greg Ewing writes: >> >> > On 6/08/21 12:00 pm, Jack Brandom wrote: >> >> It seems >> >> that I'd begin at position 3 (that's "k" which I save somewhere), then I >> >> subtract 1 from 3, getting 2 (that's "c", which I save somewhere), then >> >> I subtract 1 from 2, getting 1 (that's "a", ...), then I subtract 1 from >> >> 1, getting 0 (that's J, ...), so I got "kcaJ" but my counter is 0 not >> >> -13, which was my stopping point. >> > >> > You need to first replace any negative or missing indices with >> > equivalent indices measured from the start of the string. >> > >> > When you do that in this example, you end up iterating backwards from 3 >> > and stopping at -1. >> >> Yeah, that makes sense now. But it sucks that the rule for replacing >> negative indices is sometimes missing index and sometimes positive >> index. (That is, we can't always use positive indices. Sometimes we >> must use no index at all. I mean that's how it looks to my eyes.) > > Sometimes, the index you need to use is the value None. You cannot > use a positive number to indicate the position to the left of zero - > at least, not if you consider numbers to be on a number line. True --- I understand that now. (I think someone else in this thread had pointed that out too. But thanks for clarifying it.) -- https://mail.python.org/mailman/listinfo/python-list