Re: [Tutor] question about metaclasses
On Jan 10, 2018 18:57, Steven D'Aprano wrote: > > On Wed, Jan 10, 2018 at 04:08:04PM +, Albert-Jan Roskam wrote: > > > In another thread on this list I was reminded of > > types.SimpleNamespace. This is nice, but I wanted to create a bag > > class with constants that are read-only. > > If you expect to specify the names of the constants ahead of time, the > best solution is (I think) a namedtuple. Aaah *slaps forehead*, for some reason I didn't think about this, though I use namedtuples quite often. Using a metaclass for the very first time was great fun though :-) > from collections import namedtuple > Bag = namedtuple('Bag', 'yes no dunno') > a = Bag(yes=1, no=0, dunno=42) > b = Bag(yes='okay', no='no way', dunno='not a clue') > > ought to do what you want. > > Don't make the mistake of doing this: > > from collections import namedtuple > a = namedtuple('Bag', 'yes no dunno')(yes=1, no=0, dunno=42) > b = namedtuple('Bag', 'yes no dunno')(yes='okay', no='no way', dunno='not a > clue') But if I do: Bag = namedtuple('Bag', 'yes no dunno') ... and then I create hundreds of Bag instances, this doesn't have a large memory footprint, right? (Because of __slots__) Or is a regular tuple still (much) less wasteful? > because that's quite wasteful of memory: each of a and b belong to a > separate hidden class, and classes are rather largish objects. > > > If you expect to be able to add new items on the fly, but have them > read-only once set, that's a different story. > > > -- > Steve > ___ > Tutor maillist - Tutor@python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] question about metaclasses
On Jan 10, 2018 19:32, Peter Otten <__pete...@web.de> wrote: > > Albert-Jan Roskam wrote: > > > Why does following the line (in #3) > > > # 3- > > class Meta(type): > > def __new__(cls, name, bases, attrs): > > for attr, obj in attrs.items(): > > if attr.startswith('_'): > > continue > > elif not isinstance(obj, property): > > import pdb;pdb.set_trace() > > #setattr(cls, attr, property(lambda self: obj)) # > > #incorrect! > > raise ValueError("Only properties allowed") > > return super().__new__(cls, name, bases, attrs) > > > > class MyReadOnlyConst(metaclass=Meta): > > __metaclass__ = Meta > > YES = property(lambda self: 1) > > NO = property(lambda self: 0) > > DUNNO = property(lambda self: 42) > > THROWS_ERROR = 666 > > > > > > c2 = MyReadOnlyConst() > > print(c2.THROWS_ERROR) > > #c2.THROWS_ERROR = 777 > > #print(c2.THROWS_ERROR) > > > not convert the normal attribute int > a property? > > > > setattr(cls, attr, property(lambda self: obj)) # incorrect! > > cls is Meta itself, not MyReadOnlyConst (which is an instance of Meta). > When the code in Meta.__new__() executes MyReadOnlyConst does not yet exist, > but future attributes are already there, in the form of the attrs dict. > Thus to convert the integer value into a read-only property you can > manipulate that dict (or the return value of super().__new__()): > > class Meta(type): > def __new__(cls, name, bases, attrs): > for attr, obj in attrs.items(): > if attr.startswith('_'): > continue > elif not isinstance(obj, property): > attrs[attr] = property(lambda self, obj=obj: obj) > > return super().__new__(cls, name, bases, attrs) > > class MyReadOnlyConst(metaclass=Meta): > YES = property(lambda self: 1) > NO = property(lambda self: 0) > DUNNO = property(lambda self: 42) > THROWS_ERROR = 666 > > c = MyReadOnlyConst() > try: > c.THROWS_ERROR = 42 > except AttributeError: > pass > else: > assert False > assert c.THROWS_ERROR == 666 Thanks all for your replies! Awesome, this is exactly what I want. I think I'll also override __setattr__ so that each newly added attribute is automatically converted into a property Is a metaclass the best/preferred/only way of doing this? Or is a class decorator an alternative route? Is the following analogy for doing stuff when a class is created ('born') correct? Metaclass --> prenatal surgery __new__ --> perinatal surgery Class decorator --> postnatal surgery > PS: If you don't remember why the obj=obj is necessary: > Python uses late binding; without that trick all lambda functions would > return the value bound to the obj name when the for loop has completed. > A simplified example: > > >>> fs = [lambda: x for x in "abc"] > >>> fs[0](), fs[1](), fs[2]() > ('c', 'c', 'c') > >>> fs = [lambda x=x: x for x in "abc"] > >>> fs[0](), fs[1](), fs[2]() > ('a', 'b', 'c') > > > ___ > Tutor maillist - Tutor@python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
[Tutor] Writing to a file
Hello, I'm new to python and programming as such and as an exercise for I/O am writing a small program that reads data from a .txt file, and analyzes the text, ie, number of words/characters, avg. length of words, and frequency of words and characters. Once the text has been analyzed, the results are then written to a file. After reading the documentation, and a bit about json, I'm afraid, I've managed to completely confuse myself. When do you actually use json or pickle, I understand that with data written to .json files can be used by programs written in other languages, and pickle is for python specific objects. So are there specific objects for which .json is used or pickle is preferred? And if I'm just using write() then I'm just writing to a file, and has nothing to do with json or pickle? Also is it possible to append data to a already existing file? So far it seems that everytime I'm calling my write function, its re-writing the whole file with just the last variable called. Ive added my code below, and am currently using json.dump() as I would like to send the file to a friend who is writing a similar program but with a gui, and it would be nice if his program can read the data without problems. I realize these are pretty basic questions and am missing some basic fundamentals. I'd be grateful if someone could point me in the right direction, any tips would be highly appreciated. from collections import Counter import json class Files: def __init__(self, filename): with open(filename, 'r', encoding='utf-16') as file_input: self.file_input_string = file_input.read().replace('\n', ' ') def num_of_words(self): """ Return number of words in the file""" return str(len(self.file_input_string.split())) def num_of_keystrokes(self): """ Total number of keystrokes # abcde.. = 1 stroke # ABCDE.. = 2 strokes # '.,-/;[]=\ = 1 stroke # !@#$%^&*()_+|}{":?>< = 2 strokes """ lowercase_letters = sum(1 for c in self.file_input_string if c.islower()) uppercase_letters = sum(2 for c in self.file_input_string if c.isupper()) one_keystroke_punc = ".,-=[]\;'/ " # space included puncuation_one = sum(1 for c in self.file_input_string if c in one_keystroke_punc) two_keystroke_punc = '!@#$%^&*()_+|}{":?><' puncuation_two = sum(2 for c in self.file_input_string if c in two_keystroke_punc) return str(lowercase_letters + uppercase_letters + puncuation_one + puncuation_two) def num_of_char(self): """ Return number of characters in the string without spaces""" return str(len(self.file_input_string) - self.file_input_string.count(" ")) def frequency_of_char(self): """ Frequency of characters in the file """ count = Counter(self.file_input_string) dict_count = dict(count) print("{:<12} {:<10}".format('Character', 'Frequency')) for k, v in dict_count.items(): print("{:<12} {:<10}".format(k, v)) def frequency_of_words(self): """ Frequency of words in the file""" # word_count = Counter() # for word in self.file_input_string.replace(' ', '\n'): ### macht wider char. sollte fuer line funktioniern # word_count.update(word) # print("{:<15} {:15}".format("Word", "Frequency")) # for k, v in word_count.items(): # print("{:<15} {:<15}".format(k, v)) word_list = self.file_input_string.split() word_frequecy = [word_list.count(w) for w in word_list] ## funktioniert mit string.count!! word_frequecy_dict = dict(zip(word_list, word_frequecy)) print("{:<15} {:15}".format("Word", "Frequency")) for k, v in word_frequecy_dict.items(): print("{:<15} {:<15}".format(k, v)) def average_len_of_words(self): """ calculate the averge length of the words""" word_list = self.file_input_string.split() average = sum(len(word) for word in word_list) / len(word_list) return str(average) def write_to_file(self, data): """ collect all data for Morgen_Kinder.txt in a file""" with open('data.json', 'w') as f: json.dump(data, f, sort_keys=True, indent=4) #test x = Files('Morgen_Kinder.txt') a = Files.num_of_char(x) Files.write_to_file(x,a) print(a) b = Files.num_of_words(x) Files.write_to_file(x,b) print(b) c = Files.frequency_of_char(x) Files.write_to_file(x,c) d = Files.frequency_of_words(x) Files.write_to_file(x,d) e = Files.average_len_of_words(x) Files.write_to_file(x,e) print(e) g = Files.num_of_keystrokes(x) Files.write_to_file(x,g) print(g) ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Writing to a file
On 18/01/18 20:51, Devansh Rastogi wrote: > When do you actually use json or pickle, I understand that with data > written to .json files can be used by programs written in other languages, > and pickle is for python specific objects. Yes, that's correct. > So are there specific objects > for which .json is used or pickle is preferred? No, its more about whether you are just looking to persist an objects state (pickle) or share the data with another program(json). > write() then I'm just writing to a file, Correct. write() just send a string to a file. > Also is it possible to append data to a already existing file? Yes open in with mode 'a' instead of 'w' If no file exists it creates a new one, if one exists it appends to the end. > far it seems that everytime I'm calling my write function, its re-writing > the whole file with just the last variable called. Presumably because you open it with 'w' as the mode? > Ive added my code below, and am currently using json.dump() as I would > like to send the file to a friend who is writing a similar program but with > a gui, and it would be nice if his program can read the data without > problems. If the GUI is in Python he can use pickle but otherwise you need json (or some other portable format like csv or xml. > I realize these are pretty basic questions and am missing some basic > fundamentals. I'd be grateful if someone could point me in the right > direction, any tips would be highly appreciated. You could try reading the Handling Files topic in my tutorial http://www.alan-g.me.uk/l2p2/tutfiles.htm > from collections import Counter > import json > > class Files: > def __init__(self, filename): > with open(filename, 'r', encoding='utf-16') as file_input: > self.file_input_string = file_input.read().replace('\n', ' ') Any particular reason you use utf-16 instead of the much more common utf-8? Just curious... > def num_of_words(self): > """ Return number of words in the file""" > return str(len(self.file_input_string.split())) Actually you return the string representation of the number not the actual number. > def num_of_keystrokes(self): > """ Total number of keystrokes > # abcde.. = 1 stroke > # ABCDE.. = 2 strokes > # '.,-/;[]=\ = 1 stroke > # !@#$%^&*()_+|}{":?>< = 2 strokes """ > > lowercase_letters = sum(1 for c in self.file_input_string if > c.islower()) > uppercase_letters = sum(2 for c in self.file_input_string if > c.isupper()) > one_keystroke_punc = ".,-=[]\;'/ " # space included > puncuation_one = sum(1 for c in self.file_input_string if c in > one_keystroke_punc) > two_keystroke_punc = '!@#$%^&*()_+|}{":?><' > puncuation_two = sum(2 for c in self.file_input_string if c in > two_keystroke_punc) > > return str(lowercase_letters + uppercase_letters + > puncuation_one + puncuation_two) Again you are returning the string not the number. > def num_of_char(self): > """ Return number of characters in the string without spaces""" > return str(len(self.file_input_string) - > self.file_input_string.count(" ")) And again... > def frequency_of_char(self): > """ Frequency of characters in the file """ > count = Counter(self.file_input_string) > dict_count = dict(count) > print("{:<12} {:<10}".format('Character', 'Frequency')) > for k, v in dict_count.items(): > print("{:<12} {:<10}".format(k, v)) While this prints the valuers you don;t store them since dict_count is a local variable that gets thrown away. It might be better to store it as a classs attribute? > def frequency_of_words(self): > """ Frequency of words in the file""" > # word_count = Counter() > # for word in self.file_input_string.replace(' ', '\n'): ### > macht wider char. sollte fuer line funktioniern > # word_count.update(word) > # print("{:<15} {:15}".format("Word", "Frequency")) > # for k, v in word_count.items(): > # print("{:<15} {:<15}".format(k, v)) > > word_list = self.file_input_string.split() > word_frequecy = [word_list.count(w) for w in word_list] ## > funktioniert mit string.count!! > word_frequecy_dict = dict(zip(word_list, word_frequecy)) > print("{:<15} {:15}".format("Word", "Frequency")) > for k, v in word_frequecy_dict.items(): > print("{:<15} {:<15}".format(k, v)) > > def average_len_of_words(self): > """ calculate the averge length of the words""" > word_list = self.file_input_string.split() > average = sum(len(word) for word in word_list) / len(word_list) > return str(average) Once again you return the string rather than the value. > def write_to_file(self, data): > """ collect all data for Morgen_Kinder.txt in a file""" > with open('data.json', 'w
Re: [Tutor] question about metaclasses
On Thu, Jan 18, 2018 at 05:31:24PM +, Albert-Jan Roskam wrote: > > Don't make the mistake of doing this: > > > > from collections import namedtuple > > a = namedtuple('Bag', 'yes no dunno')(yes=1, no=0, dunno=42) > > b = namedtuple('Bag', 'yes no dunno')(yes='okay', no='no way', dunno='not a > > clue') > > But if I do: > Bag = namedtuple('Bag', 'yes no dunno') > ... and then I create hundreds of Bag instances, this doesn't have a > large memory footprint, right? (Because of __slots__) Or is a regular > tuple still (much) less wasteful? Correct. namedtuple instances are *nearly* as compact as regular tuples. Making many instances of the one named tuple class is efficient; making many named tuple classes, with one instance each, is slow and wasteful of memory. -- Steve ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor