[Tutor] Need to discuss about a class that can be used to print number of vowels, consonants, uppercase and lowercase letters and spaces in a string

Manprit Singh manpritsinghece at gmail.com
Thu Aug 19 10:54:19 EDT 2021


Dear Sir,

Based on the guidance given by all of you I have rewritten the class . Now
the problem is a little bit modified. The class should print only number of
spaces, uppercase letters and lowercase letters .The class is given below

class StringStats:
    def __init__(self, seq = ""):
        self.str_seq = seq
        self.cnt_lowercase = 0
        self.cnt_space= 0
        self.cnt_uppercase = 0
        self.compute_stats()

    def compute_stats(self):
        for ch in self.str_seq:
            if ch == " ":
                self.cnt_space += 1
            elif ch.isupper():
                self.cnt_uppercase += 1
            elif ch.islower():
                self.cnt_lowercase += 1

    def __str__(self):
        return (f"String is- {self.str_seq}\n"
                   f"Count of uppercaseletters-{self.cnt_uppercase}\n"
                   f"Count of lowercaseletters-{self.cnt_lowercase}\n"
                   f"Count of spaces -{self.cnt_space}")


s_seq = "Amar Singhji "
st1 = StringStats(s_seq)
print(st1)

Gives the desired output as follows :

String is- Amar Singhji
Count of uppercaseletters-2
Count of lowercaseletters-9
Count of spaces -2

Still i have following questions :

!) How can i place self.compute_stats() inside __init__ without
placing all those instance variables that are initialized to 0.

2) The way I have used f strings inside __str__  for multiple lines is correct ?


On Wed, Aug 18, 2021 at 2:35 PM Alan Gauld via Tutor <tutor at python.org>
wrote:

> On 18/08/2021 05:13, Manprit Singh wrote:
> > Dear sir,
> >
> > I have written below given program using a class that will print number
> of
> > vowels, consonants, uppercase and lowercase letters and spaces  in a
> > string, provided string contains only alphabets and space
>
> In general its better to keep printing separate from calculation.
> You have done that by creating a separate method but it would be
> better still to simply create a __str__() method and you can
> then just print the object. This males the class more usable
> in different  contexts - such as a GUI or Web application.
>
> > class StringStats:
> >     def __init__(self):
> >         self.str_seq = None
> >         self.uppercase = None
> >         self.lowercase = None
> >         self.vowel = None
> >         self.consonants = None
> >         self.spaces = None
>
> These initializations are not type compatible with the expected
> values. It would be better to use an empty string and zeros.
> Otherwise your new instance will be incompatible with code
> that uses it - imagine an app with a list of StringStats
> objects and that extracts all the values for calculation.
> Any object which has not had a string assigned and an explicit
> call to compute() will return Nones instead of numbers.
>
> Why not add the seq  to the constructor and then analyse
> it immediately?
>
> class StringStats:
>     def __init__(self), seq="")
>        self.seq = seq
>        self.compute_stats()
>
> That does everything your method does and more.
> You can still use the seq assignment directly if you
> need to change the string later.
>
> >     def set_string(self, seq):
> >         self.str_seq = seq
> >
> >     def count_uppercase(self):
> >         self.uppercase = sum(ch.isupper() for ch in self.str_seq)
> >
> >     def count_lowercase(self):
> >         self.lowercase = sum(ch.islower() for ch in self.str_seq)
> >
> >     def count_vowels(self):
> >         self.vowel = sum(ch in "AEIOUaeoiu" for ch in self.str_seq)
> >
> >     def count_consonants(self):
> >         self.consonants = sum(ch not in "AEIOUaeoiu " for ch in
> > self.str_seq)
> >
> >     def count_space(self):
> >         self.spaces = self.str_seq.count(" ")
>
> Given that you need to loop over the entire string for each
> method why not just have one method that sets all the counters
> in one pass of the string? The extra work over just setting one
> is minimal.
>
> >     def compute_stats(self):
> >         self.count_uppercase()
> >         self.count_lowercase()
> >         self.count_vowels()
> >         self.count_consonants()
> >         self.count_space()
>
> So this would become:
>
> def compute_stats(self):
>     for ch in self.seq:
>         if ch in string.vowels
>            self.vowels =+=1
>         elif ch.islower():
>            self.lower += 1
>         elif ch.isupper():
>            self.upper == 1
>  etc.
>
> >     def show_stats(self):
> >         print("String is-", self.str_seq)
> >         print("Count of vowels-", self.vowel)
> >         print("Count of consonants-", self.consonants)
> >         print("Count of uppercase letters-", self.uppercase)
> >         print("Count of lowercase letters", self.lowercase)
> >         print("Count of spaces", self.spaces)
>
> And this could become your __str__() method, returning
> the string instead of printing it.
>
> So the modified class creates neater client code:
>
> > s_seq = "Amar Singh"
> > st1 = StringStats()
> > st1.set_string(s_seq)
> > st1.compute_stats()
> > st1.show_stats()
>
> Becomes
>
> s_seq = "Amar Singh"
> st1 = StringStats(s_seq)
> print(st1)
>
> > Just need your comments on this class against following points :
> >
> > 1) The way i have initialized all instance variables inside __init__(),
> later
> > on assigning values to each instance variable in different methods .
>
> See above. The use of inconsistent types and the deferred assignment of
> the string leaves the class in an inconsistent state compared to its
> peers. This could cause issues in the processing of collections of
> Stringstat objects. At the very least it will require extra checks
> to see if None is returned.
>
> Much better to initialize the string in the init as a parameter.
>
> > 2) I have made different methods to set each instance variable, can't all
> > these instance variables be set in a single method ?
>
> Yes, it would be better that way. it seems very unlikely that
> a user would want to only initialise one statistic.
>
> > 3) Any further improvement in the class
> Does it need to be a class at all?
> This could just be a function that takes a string and returns
> a dict or tuple of results.
>
> It's the old rule that a class that only has an init() plus
> one method is really a function. (getter/setters and _str_()
> don't really count as methods in that context)
>
>
> --
> Alan G
> Author of the Learn to Program web site
> http://www.alan-g.me.uk/
> http://www.amazon.com/author/alan_gauld
> Follow my photo-blog on Flickr at:
> http://www.flickr.com/photos/alangauldphotos
>
>
> _______________________________________________
> Tutor maillist  -  Tutor at python.org
> To unsubscribe or change subscription options:
> https://mail.python.org/mailman/listinfo/tutor
>


More information about the Tutor mailing list