Simplified string alignment with a function? - python

I have a python program with lots of text I want to align and .format seemed messy, so I planned on using .center(shutil.get_terminal_size().columns) on the end of a print like so:
import shutil
print("Hello world!".center(shutil.get_terminal_size().columns))
and should be returned centered in my console, and it is.
However, If I want to simplify this into a single function:
def align_center():
center(shutil.get_terminal_size().columns)
and add that to the end of a print fuction:
print("Hello World!".align_center())
(I've also tried it without the parentheses at the end of .align_center)
it returns:
AttributeError: 'str' object has no attribute 'align_center'
I'm pretty sure I had it working correctly earlier but I changed something and it stopped, Am I crazy and this simplification via a function isn't possible? Am I applying it wrong?

I don't think you ever had it working if you simply appended it to the string. That would make it an atrribute and the str object is unlikely to have an attribute that matches the name of an arbitrary method you created.
This might help: pass the string to the method and return it formatted. Something like this:
def align_center(s):
return s.center(shutil.get_terminal_size().columns)
then:
print(align_center(s))

Related

String variable preventing class data from printing

Whenever I try this:
x = input(foo)
showstat(x)
def showstat(y):
print(y.thing)
It comes back as an error
'str' object has no attribute 'thing'
but I don't know how to change it's data type in order to make it function properly.
I just need to figure out how to make it read as:
print(foo.thing)
rather than the error:
print("foo".thing)
It sounds like you are trying to evaluate the input as a python expression so you can retrieve the variable with the name entered. Is this correct?
If so you can use
eval(x)
though safer would be something like
locals()[x]
I would personally do this to your input, not as part of the function, but it depends on exactly what else you're trying to do.

Python doesn't detect syntax error

I've been trying to sort an array of number using the sort function but I forgot to write parentheses.
arr.sort
instead of
arr.sort()
My question is why can't python detect this error and inform me like Java can?
The program kept compiling fine but because I was inputting the numbers in ascending order, the problem wouldn't show up.
arr.sort is syntactically valid. It's just not the syntax you wanted. Syntactically, arr.sort is an attribute access expression for the sort attribute of whatever arr evaluates to; semantically, when arr is a list, arr.sort evaluates to a method object for arr's sort method, so it's perfectly fine at runtime too.
It's kind of like method references in Java, but since Python is dynamically typed and has method objects, it doesn't need to go through all the functional interface and poly expression stuff Java 8 had to add to support list::sort syntax.
Syntax errors are only for outright invalid syntax.
Because it is not an error.
When you do not include () the function (or method) is not called. Instead it returns the function.
Example:
>>> str.encode
<method 'encode' of 'str' objects>
In actual practice:
import tkinter as tk
def hello():
print('hello')
tk.Frame()
a = tk.button(text="Press", command=hello)
a.pack()
tk.mainloop()
Now if you try it with command=hello() then it calls the function without you actually pressing the button.
The reason why Python can't detect this type of error is because Python is dynamically typed, whereas Java is statically typed.
When you say a = arr.sort, python assigns the function to a. Now you can do a() and it will run arr.sort. This is a totally valid thing to do in Python, and since we don't tell it ahead of time what a should be, it can't know whether you meant a to be a function or a sorted list... it just trusts you know what you're doing.
Java, on the other hand, is statically typed: You tell it ahead of time what a should be. Therefore, when you accidentally leave off parens, it says "that's a function, not a list like you said it would be".
If you use an IDE like PyCharm, it will tell you lots of warnings along these lines:
self.function shows:
Statement seems to have no effect and can be replaced with function call to have an effect
but the moment we assign it:
a = self.function it has an effect and this cannot be detected.

Mocking inherited methods

Please forgive my noob status, but I have come across a construct I don't really understand and hope someone can explain it for me.
class Base(object):
def mogrify(self, column):
return self.mogrifiers.get(column.lower().strip()) or (lambda x: x)
...
class MyClass(some.package.Base):
def mogrifiers(self):
return {
'column1': (lambda x: datetime.datetime.fromtimestamp(int(x)))
}
...
class MyOtherClass(object):
def convert_columns:
...
new_row[colkey] = self.myclass.mogrify(colkey)(value)
This all works, but I'm trying to write a unit test and mock out MyClass.
As far as I can tell, mogrifiers returns a dictionary of all the columns and any transformations that are required.
The code I am testing calls mogrify (inherited from the Base class) with a specific column name in a string.
This tries to extract the column from the dictionary and returns the lambda function ? or if it doesn't exist in the dictionary, it returns a lambda that just gives the string back ?
So that just leaves me with the (value) bit in the code I'm trying to test. It's no clear what it does.
If I don't want to test the underlying conversion/transformation my mock could just return the simple lambda.
So I've done that, but it throws an exception on the call to mogrify saying:
E TypeError: 'str' object is not callable
Can anyone provide some clues what I'm missing here?
As far as I can tell, mogrifiers returns a dictionary of all the
columns and any transformations that are required.
That is correct, though as you've shown it it will create a fresh dictionary each time which seems unnecessary.
The code I am testing calls mogrify (inherited from the Base class)
with a specific column name in a string.
This tries to extract the column from the dictionary and returns the
lambda function ? or if it doesn't exist in the dictionary, it returns
a lambada that just gives the string back ?
Yes, that is also correct (except that a lambada is a dance, but I think you meant lambda again).
So that just leaves me with the (value) bit in the code I'm trying to
test. It's no clear what it does.
The call self.myclass.mogrify(colkey) returns a callable, the (value) simply calls it. It may be clearer if I rewrite like this:
fn = self.myclass.mogrify(colkey)
new_row[colkey] = fn(value)
splitting it into two lines will also make it clearer whether the problem is with the call self.myclass.mogrify(colkey) or fn(value). If as seems likely it is the fn(value) call it means your mocked mogrify is returning a str instead of returning a callable; it could however be that you got the mock wrong and the mocked mogrify method is actually a string.
I would suggest you rewrite as shown and also insert a print between the two lines and see what is actually being returned.

AttributeError: instance has no attribute

This seems to be a common error in Python, and I've found many instances of people asking about similar but spent the last (long amount of time) trying those solutions where they seemed applicable and have had no luck, so resorting to asking to find out what I'm missing.
I'm receiving AttributeError: WebHandler instance has no attribute 'search_str'
It seems to be this one particular method, any time I call any of the class variables set in ___init___ from this method I receive this error. I've extracted it to a test file as a simple function rather than a class method and it works fine, and I've tried re-indenting everything a few times to make sure it wasn't that, so I'm at a loss on this.
I'm using Python 2.7 and TextWrangler if either of these are helpful (TextWrangler hasn't given me any problems in 3 years like this, but figured anything should be included)
import requests
import re
class WebHandler():
def ___init___(self):
self.urllist = []
self.search_str = re.compile(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_#.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', re.I|re.M)
def set_urls(self, test, data):
for line in test[11:]:
if (("even" in line) or ("odd" in line)):
match = re.search(self.search_str, line)
self.urllist.append(match.group(0))
Another thing I tried, if I copy the attributes from ___init___ and simply make them local to set_urls() and call them without self that way it works properly and doesn't throw any errors, which is confusing me even more.
No idea what I'm missing. Thanks!
Your init function has three underscores:
def ___init___(self):
It should have only two:
def __init__(self):
As it is written now, it is not being called when you create a new object.
This that you have:
def ___init___(self):
Is not the same as this that gets called when an object is instantiated:
def __init__(self):
The difference is that you have three underscores on either side of init, while two are required.

Why am I getting this TypeError?

RESOLVED: Okay, you guys probably won't believe this. I did a lot of digging and it turns out that all the files we are loading and using were created incorrectly. The files fail to conform with the code we are writing — the things we want to do in our program are simply not possible based on the current state of the files we load. I am currently working on fixing this. Sorry about the non-question, guys!
In Python I have code that essentially reads as follows:
partsList = getPartsList() # this function returns a list
for part in partsList:
...
bar(partsList)
def bar(partsList):
for part in partsList:
...
But when I run the code I get the following TypeError:
TypeError: iteration over non-sequence
This TypeError is in reference to the noted line:
def bar(partsList):
for part in partsList: # this is the line with the TypeError
...
How can this be? I know that partsList is not a non-sequence because just before my program calls bar(partsList), I explicitly iterate over partsList.
My function does not modify partsList before interacting with it, and I do not modify partsList when iterating through it prior to calling the function, yet somehow it changes from a list to a non-sequence when the function is called.
I am working entirely within a class so these are all methods actually; I just thought it would be easier to read if I present the code this way.
The following is in response to the comments:
I wish I could provide you all with the full code, but at the moment the program requires exactly 275 files to run and has 20+ .py files. I will mention that the method in question does employ recursion after iteration through its given list. I thought this may be linked to the error, but when when attempting to print the list itself and its contents, the program gave the same TypeError before making it through the method even once, so I know that this is not due to the recursion; it never even recursed.
Ok I have inserted print statements as follows (keep in mind these are within methods in a class):
def someMethod(self):
...
partsList = self.getPartsList() # this function returns a list
for part in partsList:
...
print partsList # prints [object(1), object(2)]
self.bar(partsList)
def bar(self, partsList):
print partsList # prints <filename.class instance at 0x04886148>
for part in partsList: # still gives me the TypeError
...
When I say filename.class I don't literally mean filename and class. You guys know what I mean.
Is the second print statement printing <filename.class instance at 0x04886148> because it is pointing to the actual partsList? I'm not entirely sure how pointers and references work in Python.
You don't define bar correctly; its first argument is a reference to the object that calls it, and the second argument is the list you pass as the explicit argument.
def bar(self, partsList):
for part in partsList:
...
Your answer is there in the print lines.
def bar(self, partsList):
print partsList # prints <filename.class instance at 0x04886148>
for part in partsList: # still gives me the TypeError
...
partsList isn't a list going into this method. Here is some tweaked, functioning, example code from your code:
class myClass():
def someMethod(self):
partsList=self.getPartsList()
for part in partsList:
print part
self.bar(partsList)
def bar(self, pList):
print pList
for part in pList:
print part
def getPartsList(self):
return ['a', 'b', 'c']
Running this interactively gets me this:
from fake_try import myClass
x = myClass()
x.someMethod()
a
b
c
['a', 'b', 'c']
a
b
c
You'll notice that when I called "print pList" I received a pretty print output of the list. You are receiving an object of your class type.
I understand and empathize with your situation. Having a large, complex program throwing errors can be quite painful to debug. Unfortunately without seeing your entire code I don't think anyone here will be able to debug your issue because my guess is that you are calling 'someMethod' in a way that is unexpected in the actual code(or in an unexpected place) which is causing you to have issues.
There are a couple of ways you can debug this.
I am assuming that everything ran UNTIL you added the someMethod functionality? Revert your code to a state prior to the error and add lines on at a time(with dummy functions if neccesary) to find exactly where the unexpected value is coming from. If you cannot revert my first step would be to simplify all logic surrounding this issue. You have a function 'getPartsList()' that's supposed to return a list. It looks like it is here, but make it even easier to check. Make a dummy function that simply returns a fake list and see what the behavior is. Change things one step at a time until you iron out where the issue is.
You may not be familiar with the inspect module. Try importing inspect in your module and using inspect.getmember(x) with x being the object you want more information about. I would probably use this in place of you print partsList in the bar method( something like inspect.getmember(partsList) ) I would guess that you're somehow passing a class there instead of the list, this should tell you what that class has been instantiated as.

Categories