6

My question is about how to deal with the piece of code where I am using the Caesar´s cipher.

Functions Decrypt and Encrypt have to deal with the limits of the alphabet (A - Z and a - z). I tried to write the two possible cycles for both alphabets in one cycle function named cycleencrypt.

But the function takes about 6 arguments and I have read somewhere that is less readable and understandable having more than 3 arguments in one function so my question is:

Should I reduce the number of arguments by splitting in two functions and make the piece of code longer (but maybe more understandable)? Thanks for any answer I aprreciate that.

EDIT: Docstrings around the functions were deleted to make visible the main purpose of my question.

def offsetctrl(offset):
    while offset < 0:
        offset += 26
    return offset

def cycleencrypt(string, offset, index, listing, first, last):
    offset = offsetctrl(offset)
    if string >= ord(first) and string <= ord(last):
        string += offset
        while string > ord(last):
            string = ord(first) + (string - ord(last) -1)
        listing[index] = chr(string)         

Cycle for encrypting with a lots of arguments and control of negative offset´s

def encrypt(retezec, offset):
    listing = list(retezec)
    for index in range(0, len(retezec)):
        string = ord(retezec[index])
        cycleencrypt(string, offset, index, listing, 'A', 'Z')
        cycleencrypt(string, offset, index, listing, 'a', 'z')
    print(''.join(listing))

main encryption part taking many arguments in two lines with printing

def decrypt(retezec, offset):
    return encrypt(retezec, -offset)

if __name__ == "__main__":
encrypt("hey fellow how is it going", 5)
decrypt("mjd kjqqtb mtb nx ny ltnsl", 5)
luky suky
  • 63
  • 1
  • 5

3 Answers3

9

In this kind of situation, it's often better to write your code as a class. Your class's constructor could take just the minimum number of arguments that are required (which may be none at all!), and then optional arguments could be set as properties of the class or by using other methods.

When designing a class like this, I find it's most useful to start by writing the client code first -- that is, write the code that will use the class first, and then work backwards from there to design the class.

For example, I might want the code to look something like this:

cypher = Cypher()
cypher.offset = 17
cypher.set_alphabet('A', 'Z')
result = cypher.encrypt('hey fellow how is it going')

Hopefully it should be clear how to work from here to the design of the Cypher class, but if not, please ask a question on Stack Overflow about that!

If you want to provide encrypt and decrypt convenience methods, it's still easy to do. For example, you can write a function like:

def encrypt(text, offset):
    cypher = Cypher()
    cypher.offset = offset
    return cypher.encrypt(text)
Daniel Pryden
  • 59,486
  • 16
  • 97
  • 135
  • Ah I see thanks a lot, I´ve just started with programming in Python at the school and I have writtten all my tasks in the functions only so far and I was not thinking about the Classes as in other languages. Thanks for your answer, I will try to write some simple classes to make the program looking better – luky suky Nov 03 '17 at 13:19
  • @lukysuky: Glad to help, and welcome to Stack Overflow! If this answer or any other one solved your issue, please mark it as accepted. – Daniel Pryden Nov 03 '17 at 13:25
  • 2
    I'll just leave [this](https://stackoverflow.com/questions/2078978/functional-programming-vs-object-oriented-programming) here. – Art Nov 03 '17 at 13:28
  • @Art: Don't get me wrong, functional is a great paradigm (and far superior for many kinds of problems). But in Python where there's no built-in syntax for partial application, you end up with this problem of a combinatory explosion of arguments; I find objects to be the idiomatic way to solve that in this language. (If this question were about Haskell, my answer would be different, of course. :-) – Daniel Pryden Nov 03 '17 at 13:31
3

Here is the docstring of datetime.datetime:

class datetime(date):
    """datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
    ...
    """

And the signature of its constructor:

def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0, microsecond=0, tzinfo=None):

What we could learn from it:

  • Add exactly as many arguments as it makes sense to add
  • Use parameters and to give sensible default values to arguments

Side thought: do you think users of your library would should use cycleencrypt()? You could mark it private (with underscore), so everybody will see it's not a public API and they should use encrypt() and decrypt() instead.

Art
  • 2,235
  • 18
  • 34
0

The number of arguments doesn't really matters as long as there are not a dozen of them (maybe someone can link to what you mention about having more than 3 arguments, I may be wrong).

To be more readable in the definition of a function, write comments by following docstrings convention.

To be more readable at the call of a function, gives default values in the definition as much as possible for the more useful values (for example, offset can have the value 1 by default, and index 0).

Either way, for a long line, use PEP8 guidelines which describes a way to jump lines correctly (the lines must not exceed 80 characters, according to PEP8).

def cycleencrypt(string, offset=1, index=0,
                     listing, first, last):
        """Description

        :param string: description
        :param offset: description
        :param index: description
        :param listing: description
        :param first: description
        :param last: description
        :return description
        """
        offset = offsetctrl(offset)
        if string >= ord(first) and string <= ord(last):
            string += offset
            while string > ord(last):
                string = ord(first) + (string - ord(last) - 1)
            listing[index] = chr(string)    
jgrant
  • 1,273
  • 1
  • 14
  • 22
AlEmerich
  • 430
  • 4
  • 13
  • 1
    I had some short docstrings around the functions following the pylint´s abilities but I deleted them here to make the code more visible for my purpose of this question – luky suky Nov 03 '17 at 13:28
  • Ok :) I suggest to edit your question with that information, I see two of us answering to you about that. – AlEmerich Nov 03 '17 at 13:32