Re: [Tutor] question about metaclasses

2018-01-18 Thread Albert-Jan Roskam

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

2018-01-18 Thread Albert-Jan Roskam

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

2018-01-18 Thread Devansh Rastogi
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

2018-01-18 Thread Alan Gauld via Tutor
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

2018-01-18 Thread Steven D'Aprano
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