[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

Alan Gauld alan.gauld at yahoo.co.uk
Wed Aug 18 05:04:59 EDT 2021


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




More information about the Tutor mailing list