python class operations on members vs static methods - python

I need some help figuring out the right OOP design choice for my python class.
In my example, letters are appended to a string in separate steps. The string is then printed. For this application, it is not really important to the user to get the intermediate results.
So is it better to pass the string to the constructor and then use private methods for the operations and a dothings method calling them all?
class A:
def __init__(self, string: str()):
self.string = string
def _append_a(self):
self.string += "a"
def _append_b(self):
self.string += "b"
def dothings(self):
_append_a()
_append_b()
def export(self):
print(self.string)
Or is it better to have the string passed to each method?
class AA:
#staticmethod
def append_a(string):
string += "a"
return string
#staticmethod
def append_b(string):
string += "b"
return string
#staticmethod
def export(string):
print(string)
The interface of A looks a bit cleaner to me, one can just call dothings and then export.
However, class A would be a bit of a black box, while with class AA the user has some more insights to what is happening.
Is there a 'right' choice for this?

AA is easily dismissed. There is nothing object-oriented about it: it's just three regular functions collected into a single namespace. There's no shared state operated on by a set of methods. There's no suggestion that only the output of one function is a valid input to another. For example, the intention is probably to write something like
export(append_a(append_b("foo"))) # fooba
but nothing requires this pattern be followed. The functions aren't related to each other in anyway.
A has some things in common with the builder pattern. Given an initial string, you can append as and bs to it, but nothing else (without violating encapsulation provided by the methods. Eventually, you get the "final" value by calling export, so the work flow it represents is something like:
a = A("foo")
a.append_a()
a.append_a()
a.append_b()
a.append_b()
a.append_a()
a.export() # fooaabba
The class as shown is almost trivially simple, but demonstrates how to provide a well defined interface to building a string value from an initial seed. You can't just do anything you like with it: you can't prepend values, you can't remove existing characters, etc.
To conform more closely to the builder pattern, I would modify A as follows:
class A:
def __init__(self, string: str):
self.string = string
def append_a(self):
self.string += "a"
def append_b(self):
self.string += "b"
def append_ab(self):
self.append_a()
self.append_b()
def export(self):
return self.string + "c"
As long as you don't access the string attribute directly, this class limits the kind of string you can build:
You can start with an arbitrary stem (the argument to __init__)
You can append an a to the string
You can append a b to the string
You can append an ab to the string (but this is just a convenient shortcut for calling append_a followed by append_b, as the implementation implies)
You can end the string with c
You get your final value by calling export (which I modified just to make the point that you cannot add a c at any point, so there's no way to follow a c with another a, for example).
In some sense, it's kind of a dual to a regular expression: instead of recognizing whether or not a string matches the regular expression .*(a|b)*c, you create a string that will match the regular expression.

Related

Is there a way to change a class variable without adding 'foo = '?

I have a class, and would like to change an object of it (similar to the pop method of lists), without adding an foo = foo.bar()
In simpler terms, i'd like to do foo.bar() instead of foo = foo.bar(). Is this possible in python?
Here's some code that i have, which hopefully furthers understanding:
class mystr(str):
def pop(self, num):
self = list(self)
changed = self.pop(num) # The particular character that was removed
self = ''.join(self) # The rest of the string
# Somewhere in here i need to be able to change the actual variable that pop() was called on
return changed # Emulates python lists' way of returning the removed element.
my_var = mystr("Hello World!")
print(my_var.pop(4) # Prints 'o', as you would expect
print(my_var) # But this still prints 'Hello World!', instead of 'Hell World!'
# It isn't modified, which is what i want it to do
You can, but not with str.
What you're looking for is a way to mutate your object. For most classes you write yourself, doing that is straightforward:
class Foo:
def __init__(self):
self.stuff = 0
def example(self):
self.stuff += 1
Here, calling example on a Foo instance mutates it, by changing its stuff instance attribute.
str, however, is immutable. It stores its data in C-level data structures and provides no mutator methods, so there's no way to modify its data. Even if you used ctypes to bypass the protection, you'd just get a bunch of memory corruption bugs.
You can add your own attributes in a subclass, and those will be mutable, but if you do that to fake a mutable string, you might as well just not inherit from str. Inheriting from str in that case will only cause bugs, with some code looking at your "fake" data and other code looking at the "real" underlying str data.
Most likely, the way to go will be one of two options. The first is to just use regular strings without your subclass or the methods you want to add. The second is to write a class that doesn't inherit from str.
You could achieve that by encapsulating a string, rather then inheriting from it:
class mystr:
def __init__(self, string):
self._str = string
def pop(self, num):
string_list = list(self._str)
changed = string_list.pop(num) # The particular character that was removed
self._str = ''.join(string_list) # The rest of the string
return changed # Emulates python lists' way of returning the removed element.
def __repr__(self):
return self._str
Running the same code with this class instead will print:
o
Hell World!

Can a Python class's __str___() return one of two strings?

I have a class for which I want to be able to print either a short string representation of an object or a longer string representation. Ideally, __str__() would accept a flag that chooses which string to return, and print() would accept that flag as well and use the correct version of __str__() accordingly, but nothing like that seems to exist.
I know that I can include print_short() and print_long() methods inside my class to choose the correct string, but this doesn't seem Pythonic, and violates the Python 3 change by which print() is a function. This would also bypass the use of __str__(), which again, seems unPythonic.
What's the most Pythonic way of handling this? Solutions involving __repr__() won't work, since I'm already using __repr__() as intended, to unambiguously represent the object itself.
The job of str is to provide "the" string representation of an object, whatever representation you decide is most useful.
If you want to control the formatting of an object, override __format__.
class MyClass:
def __format__(self, spec):
...
If you have code like
s = MyClass()
print("{:r}".format(s))
s.__format__ receives everything after the colon (in this case r) as its spec parameter; it is then entirely up to the definition of __format__ how it uses the spec in deciding what string value to return. You could do something like the following
class MyClass:
def __format__(self, spec):
if spec == 's':
return self._short_str()
elif spec == 'l':
return self._long_str()
else:
# This includes both no spec whatsoever, which is
# conventionally expected to behave like __str__
# and an unrecognized specification, which is just ignored.
return str(self)
def _long_str(self):
return "three"
def _short_str(self):
return "3"
def __str__(self):
return "III"
>>> x = MyClass()
>>> str(x)
'III'
>>> "{}".format(x)
'III'
>>> "{:whatever}".format(x)
'III'
>>> "{:s}".format(x)
'3'
>>> "{:l}".format(x)
'three'

Not able to print an array in python after appending values to it [duplicate]

So I have a class, called Vertex.
class Vertex:
'''
This class is the vertex class. It represents a vertex.
'''
def __init__(self, label):
self.label = label
self.neighbours = []
def __str__(self):
return("Vertex "+str(self.label)+":"+str(self.neighbours))
I want to print a list of objects of this class, like this:
x = [Vertex(1), Vertex(2)]
print x
but it shows me output like this:
[<__main__.Vertex instance at 0xb76ed84c>, <__main__.Vertex instance at 0xb76ed86c>]
Actually, I wanted to print the value of Vertex.label for each object.
Is there any way to do it?
If you just want to print the label for each object, you could use a loop or a list comprehension:
print [vertex.label for vertex in x]
But to answer your original question, you need to define the __repr__ method to get the list output right. It could be something as simple as this:
def __repr__(self):
return str(self)
If you want a little more infos in addition of Daniel Roseman answer:
__repr__ and __str__ are two different things in python. (note, however, that if you have defined only __repr__, a call to class.__str__ will translate into a call to class.__repr__)
The goal of __repr__ is to be unambiguous. Plus, whenerver possible, you should define repr so that(in your case) eval(repr(instance)) == instance
On the other hand, the goal of __str__ is to be readable; so it matter if you have to print the instance on screen (for the user, probably), if you don't need to do it, then do not implement it (and again, if str in not implemented will be called repr)
Plus, when type things in the Idle interpreter, it automatically calls the repr representation of your object. Or when you print a list, it calls list.__str__ (which is identical to list.__repr__) that calls in his turn the repr representaion of any element the list contains. This explains the behaviour you get and hopefully how to fix it
def __ str __ (self):
return f"Vertex: {self.label} {self.neighbours}"
#In most cases, this is probably the easiest and cleanest way to do it. Not fully sure how this code will interact with your list []. Lastly, any words or commas needed, just add them between the brackets; no further quotes needed.

TypeError for a function with a string parameter

I am trying to create a simple function that allows me to return a string reversed. However, when I call the function, the error
TypeError: reverseString() takes 1 positional argument but 2 were given
comes up. I'm more familiar with Java, and was wondering what the problem is and if passing a string parameter is the same in Python.
Class myString()
def reverseString(string):
return string[:,:,-1]
p = myString()
p.reversedString('Eric')
Python requires a special first parameter for methods to which it passes the instance that the method is being called on. You can use pretty much any valid variable name, but it's a Python convention to use self and it will make your code easier for other people to read.
class myString:
def reverseString(self, string):
return string[::-1]
p = myString()
print(p.reverseString('Eric'))
To access instance variables, you would use self.var - you can't just do var like in Java.
Also, as #jonrshape pointed out in the comments, you don't need to wrap functions in a class:
def reverseString(string):
return string[::-1]
print(reverseString('Eric'))

Python OOP init

class Sequence:
TranscriptionTable = {
"A":"U",
"T":"A",
"C":"G",
"G":"C"
}
def __init__(self, seqstring):
self.seqstring = seqstring.upper()
def transcription(self):
tt = ""
for x in self.seqstring:
if x in 'ATGC':
tt += self.TranscriptionTable[x]
return tt
DangerousVirus = Sequence('atggagagccttgttcttggtgtcaa')
print(DangerousVirus.transcription())
Hi,
I just want some clarification as to how data flows through a class. For instance, is the data in () in DangerousVirus = Sequence('atggagagccttgttcttggtgtcaa') self or seqstring?
I'm confused as to how init can have 2 variables when theres only 1 in the (). Wouldnt that mean that only self contains the sequence and seqstring is empty?
Thanks for the help! (:
self is a reference to a Sequence which is being initialized. The data string is passed as seqstring. You can see this by adding a line to print it:
print(seqstring)
The __init__ method does indeed take two arguments, but once an instance is created the self argument is "bound" to the instance (__init__ becomes a so called bound method of the instance), so you don't have to specify the instance itself anymore. If you call the unbound __init__ function from the class like this
Sequence.__init__(instance, seqstring)
you indeed have to specify the instance explicitly. The name self is just a convention, it could be anything in the definition. Take a look at the tutorial section on instance methods where this is explained.
As the other answers have said, the self arg gets passed automatically to method calls. So you must include it as the first arg in the method definition, but you must not include it in the method call.
However, there's no need to define a class for this, a simple function is sufficient. And you can use the built-in str.translate method to perform the transcription very efficiently. For large sequences, this is much faster than doing it with a Python loop as in your transcription method, since most of the work is done by compiled code, so it runs as fast as if it were written in C, not Python.
trans_table = str.maketrans('ATCG', 'UAGC')
def transcribe(seq):
seq = seq.upper()
return seq.translate(trans_table)
seq = 'atggagagccttgttcttggtgtcaa'
print(transcribe(seq))
output
UACCUCUCGGAACAAGAACCACAGUU
As mentioned in the docs, any chars that aren't in the translation table will remain unchanged in the output string. Eg,
print('abcdABCD'.translate(trans_table))
output
abcdUBGD

Categories