Class X not have attributes Y - python

I don't understand this strange things...I've this class:
class cChallenge:
def __init__(self, Id:int = 0 , Difficulty:str = '', Title:str = '',
Challenge:str = '', Url:str = '', Solved:bool = False):
self.Id = Id
self.Difficulty = Difficulty
self.Title = Title
self.Challenge = Challenge
self.Url = Url
self.Solved = Solved
Everything works fine so I've pushed all my generated cChallenge elements into a list: cList.
If I query the list for single object everything works fine:
print(cList[1].Id)
>> 380
but if I use list comp:
print([x.Id for x in cList])
>> AttributeError: type object 'cChallenge' has no attribute 'Id'
[https://pastebin.com/cGuhPAG8] That's the link if someone want to try

In the code of the complete script to fill the list you need to create an instance of a class i.e. each object (look at added variable c):
for s in submission:
#other code...
c = cChallenge(Id, Difficulty, '', Url, Solved)
cList.append(c)
#other code...
Don't know exactly what are you trying to put into the list, but probably this will do.

Just like #Alecx note
to fill the list you need to create an instance of a class
so the right code for who look inside the pastebin is:
for s in submission
...
_ = cChallenge(...)
cList.append(_)
I will post the github link to the project tomorrow for who is interested!

Related

Printing instances of class through inherited method

I am trying to do some simple stuff with OOP and Python, building some simple books service. I am trying to create class for Books, where each object will be a Book with attributes, that works, it is not the best, but good enough for now.
# Definition of class
class Books(object):
book_id = ""
book_title = ""
book_author = ""
book_year = ""
book_pub = ""
book_ac = ""
book_date = ""
Books = [book_id, book_title, book_author,
book_year, book_pub, book_ac, book_date]
# Set constructor
def __init__(self, book_id, book_title, book_author, book_year, book_pub, book_ac, book_date):
self.book_id = book_id
self.book_title = book_title
self.book_author = book_author
self.book_year = book_year
self.book_pub = book_pub
self.book_ac = book_ac
self.book_date = book_date
# Set attributes
def set_book_id(self, book_id):
self.book_id = book_id
def set_book_title(self, book_title):
self.book_title = book_title
def set_book_author(self, book_author):
self.book_author = book_author
def set_book_year(self, book_year):
self.book_year = book_year
def set_book_pub(self, book_pub):
self.book_pub = book_pub
def set_book_ac(self, book_ac):
self.book_ac = book_ac
def set_book_date(self, book_date):
self.book_date = book_date
# Show attributes
def show_book_id(self):
print(self.book_id)
def show_book_title(self):
print(self.book_title)
def show_book_author(self):
print(self.book_author)
def show_book_year(self):
print(self.book_year)
def show_book_pub(self):
print(self.book_pub)
def show_book_ac(self):
print(self.book_ac)
def show_book_date(self):
print(self.book_date)
# Show book
def show_book(self):
print(self.book_id)
print(self.book_title)
print(self.book_author)
print(self.book_year)
print(self.book_pub)
print(self.book_ac)
print(self.book_date)
Second Class is Booklist, which should hold the books inside and manage books inside, I am trying to create some basic methods as to create objects in Booklist automatically as I create Books object(this should work). Then I would like to operate with Booklist as with inherited methods from Books class to show the Books or etc.
# Definition of class
class BookList(Books):
listbook = []
# Set constructor
def __init__(self):
self.listbook = []
# Add book
def add_book(self, Books):
return self.listbook.append(Books)
# Show book
def show_books(self):
for b in self.listbook:
super().show_book(self)
# Search book
def search_book_by_id(self):
self.listbook = []
for Books in self.book_id:
if Books.book_id == id:
id.append(Books)
return id
Bellow are my tests, as I am still fighting with error like issues of parsing arguments-Expected one or more arguments from would anyone help here what would be the easiest please to use inherited method or to manipulate with inheritance for this use case please? Thank you
# Books defining
book1 = Books('1504234', 'Murders', 'Peter Jacob',
'2015', 'Ikaro', '5', '25.5.2015')
book2 = Books('1504231', 'Murders', 'Peter Jacob',
'2015', 'Ikaro', '5', '25.5.2015')
book3 = Books('1504232', 'Hiden language', 'Samuel Fellow',
'2005', 'Parik', '3', '21.4.2006')
book4 = Books('1504233', 'Saturday', 'Filp Sapko',
'2012', 'Satik', '4', '25.3.2012')
book1.show_book()
book1.set_book_title('New Murders')
book1.show_book()
book1.show_book_ac()
print("Booklist ******************************")
BookList.listbook.append(book1)
BookList.listbook.append(book2)
BookList.listbook.append(book3)
BookList.listbook.append(book4)
(Until here it works)Now the issue begins:
BookList.show_books()ERROR:
TypeError: show_books() missing 1 required positional argument: 'self'
Would anyone please tell me what I am doing wrong here?
Thank you very much!
Best Regards
To start with, I think you're imagining the structure of these classes a little wrong. The "Books" class shouldn't be the parent of "BookList" if you intend to use the super() method to access "show_books". Not to worry, you're almost there already
you only need to change it from this:
#Show book
def show_books(self):
for b in self.listbook:
super().show_book(self)
to this:
#Show book
def show_books(self):
for b in self.listbook:
b.show_book()
Your "search_book_by_id" method will also need to change from this:
#Search book
def search_book_by_id(self):
self.listbook = []
for Books in self.book_id:
if Books.book_id == id:
id.append(Books)
return id
to this:
#Search book
def search_book_by_id(self, id):
id_books = []
for b in self.listbook:
if b.book_id == id:
id_books.append(Books)
return id_books
Though I'm not sure exactly what that function was attempting before.
You also said you wanted the BookList class to automatically add books when you create them, there are far more complicated ways to link classes which I think you might not need, if all you need is for the Book class to live inside the BookList class, then I would suggest making a create_book function inside the BookList class that adds the book to the list once created, that way you only use one object. That would look something like this:
def create_book(self,book_id,book_title,book_author,book_year,book_pub,book_ac,book_date):
self.listbook.append(Books(book_id,book_title,book_author,book_year,book_pub,book_ac,book_date))
That's a pretty messy function, and you should probably look into better ways to handle objects with many attributes, checkout the #property decorator and look for other ways to store data than attributes, you can also use (*args, **kwargs) to really make things easy on the user of the function, but if you're very new to python, sometimes keeping things messy but simple is better to learn the basics with. Let me know if that helps or you need something else!

Calling a function from a class in main

I seem to be making a stupid mistake that I cant find. Im simply trying to call my functions from my record class and having an invalid syntax error despite looking at sample code and trying to emulate the syntax.
Ive tried following tutorials and calling the function in every which way so the problem may not be in the calling of the function but something else I feel.
class definitions
class record:
def __init__(self,telephone,lastname,firstname):
self.telephone = telephone
self.lastname = lastname
self.firstname = firstname
def addrecord(self,x,y,z):
x = input('Enter telephone number')
y = input('Enter lastname')
z = input('Enter firstname')
phonebook.append(record(x,y,z))
return
def deleterecord(self,x):
phonebook[x-1].pop
return
Main
phonebook = record[]
addrecord(515,'fin','matt')
print(phonebook[0].firstname)
deleterecord(1)
print(phonebook[0].firstname)
If all this works I expect the output to be
"matt"
"null"
There are a number of problems with your code:
you are defining phonebook otuside of the class
in deleterecord you should call phonebook.pop(x).
there should be two classes that handle the phonebook and records, and the record could be modeled using a namedtuple.
there are syntax errors like calling record[] which is not valid Python.
Alternative implementation:
from collections import namedtuple
PhoneRecord = namedtuple("PhoneRecord", ['firstname', 'lastname', 'telephone'])
class PhoneBook:
def __init__(self):
self._phonebook = []
def addrecord(self, record):
self._phonebook.append(record)
return self._phonebook.index(record)
def deleterecord(self, i):
self._phonebook.pop(i)
phonebook = PhoneBook()
record_index = phonebook.addrecord(PhoneRecord(firstname="matt", lastname="snow", telephone="25512521"))
print(phonebook._phonebook)
phonebook.deleterecord(record_index)
print(phonebook._phonebook)
which will yield in the console:
[PhoneRecord(firstname='matt', lastname='snow', telephone='25512521')]
[]
The simplified version of your question is, given code
records = []
records.append("matt")
print(records[0])
del records[0]
print(records[0])
why don't I get the following output
"matt"
None
Instead, you get an IndexError exception.
The reason is that you are accessing an element beyond the size of the list, and Python handles this by raising an exception rather than returning None.

How to create object from another object in Python unit test

I'm having trouble understanding unit testing in Python. I have an object, retailer, which creates another object, deal. deal refers to an attribute created in retailer, so I'm passing it a reference:
class deal():
def __init__(self, deal_container, parent):
The deal_container attribute also comes from retailer, which calls its own methods to create it. So how do I create everything I need to easily make a deal object?
Do I have to create an instance of retailer in my unit test and then call the method in that object that creates deal?
Can I use FactoryBoy to create an instance of retailer and how do I include the method that creates deal in that object?
What's the best way to approach this?
Here's the unit test. I'm setting up the soup_obj I need to give deal:
class TestExtractString(TestCase):
fixtures = ['deals_test_data.json']
def setUp(self):
with open('/home/danny/PycharmProjects/askarby/deals/tests/BestBuyTest.html', 'r') as myfile:
text = myfile.read().replace('\n', '')
self.soup_obj = bs4.BeautifulSoup(text,"html.parser")
self.deal = self.soup_obj.find_all('div',attrs={'class':'list-item'})[0]
def test_extracts_title(self):
z = Retailer.objects.get(pk=1)
s = dealscan.retailer(z)
d = dealscan.deal(self.deal,s)
result = d.extract_string(self.deal,'title')
and here's the relevant bit of the deal class in dealscan. There's a retailer class that creates a deal, but I haven't even written the bit in retailer that creates deal yet. I'm hoping I can mock the bits I need for deal without having to invoke retailer at all, but then how do I deal with the fact that deal references retailer?
class deal():
def __init__(self, deal_container, parent):
'''
Initializes deal object
Precondition: 0 > price
Precondition: 0 > old_price
Precondition: len(currency) = 3
:param deal_container: obj
'''
self.css = self.parent.css
self.deal_container = deal_container
self.parent = parent
self.title = self.extract_string('title')
self.currency = self.parent.currency
self.price = self.extract_price('price')
self.old_price = self.extract_price('old_price')
self.brand = self.extract_string('brand')
self.image = self.extract_image('image')
self.description = self.extract_string('description')
#define amazon category as clearance_url
#define all marketplace deals
def __str__(self):
return self.title
def extract_string(self, element, deal):
'''
:param object deal: deal object to extract title from
:param string element: element to look for in CSS
:return string result: result of string extract from CSS
'''
tag = self.css[element]['tag']
attr = self.css[element]['attr']
name = self.css[element]['name']
result = deal.find(tag, attrs={attr: name})
if result:
if element == 'title':
return result.text
elif element == 'price':
result = self.extract_price(result).text
if result:
return result
elif element == 'image':
result = self.extract_image(result)
return False
The problem is that the deal object is referencing the parent before it sets the self.parent attribute. Use:
self.parent = parent
self.css = self.parent.css
self.deal_container = deal_container
and the AttributeError goes away.
As for the question about whether it's good form to use an object to create another object in a unit test, the answer is that you can use mocks, but it's fine to do it this way. Using a helper method to set up the parent object once in setUp is acceptable and will make the code easier to read, and may improve test performance a little.

Accessing elements of lists, and calling their fuctions

Here is Customer class:
class Customer:
def __init__(self, timestamp, cid, item_count):
self.time_stamp = timestamp
self.customer_name = cid
self.item_count = item_count
def checkout(self, new_timestamp):
self.time_stamp = new_timestamp
def get_cus_name(self):
return self.customer_name
If I create an empty list of Customer objects like:
customers = [Customer]
And then somewhere else I try to call Customer methods in a loop like:
def checkout_customer(self, cid):
for cus in self.customers:
if cus.get_cus_name == cid:
cus.checkout(self.cur_num_customers + 7)
why do I get an error when I try to call cus.checkout? My ide tells me that it expects a Customer but got an int. Why doesn't it pass itself into the 'self' arg here?
However if I just create a Customer object and directly call its methods, it works fine:
def foo(self):
cus = Customer(1,'pop',2)
cus.checkout(23)
This is my first time learning python, and ive been stuck trying to figure out lists, and accessing its members. Perhaps my initialization of self.custormers = [Customer] is incorrect?
EDIT:
In my constructor of tester class I create an empty list like this:
self.customer = [Customer]
I am able to add customers no problem:
def add_custormer(self, customer):
self.customers.append(customer)
My problem is not adding customers, but accessing their methods once they are in a list. Doing something like this self.customers[0].checkout(1,'pop',2) gives me an error "Expected type 'Customer' got int".
I am not sure of the class where checkout_customer lives but I am assuming you declare the list self.customers somewhere in it.
self.costumers = []
If you intend to add an element Customer to the list you should use something like: self.customers.append(Customer(x,y,z)) since you want to add a new customer to the list and when doing so you are required to initialize the Customer class.
I didn't try the code but I believe something like this should work:
def foo(self):
self.customers.append(Customer(1,'pop',2))
self.checkout_customers(23)

Class design: Last Modified

My class:
class ManagementReview:
"""Class describing ManagementReview Object.
"""
# Class attributes
id = 0
Title = 'New Management Review Object'
fiscal_year = ''
region = ''
review_date = ''
date_completed = ''
prepared_by = ''
__goals = [] # List of <ManagementReviewGoals>.
__objectives = [] # List of <ManagementReviewObjetives>.
__actions = [] # List of <ManagementReviewActions>.
__deliverables = [] # List of <ManagementReviewDeliverable>.
__issues = [] # List of <ManagementReviewIssue>.
__created = ''
__created_by = ''
__modified = ''
__modified_by = ''
The __modified attribute is a datetime string in isoformat. I want that attribute to be automatically to be upated to datetime.now().isoformat() every time one of the other attributes is updated. For each of the other attributes I have a setter like:
def setObjectives(self,objectives):
mro = ManagementReviewObjective(args)
self.__objectives.append(mro)
So, is there an easier way to than to add a line like:
self.__modified = datetime.now().isoformat()
to every setter?
Thanks! :)
To update __modified when instance attributes are modified (as in your example of self.__objectives), you could override __setattr__.
For example, you could add this to your class:
def __setattr__(self, name, value):
# set the value like usual and then update the modified attribute too
self.__dict__[name] = value
self.__dict__['__modified'] = datetime.now().isoformat()
Perhaps adding a decorator before each setter?
If you have a method that commits the changes made to these attributes to a database (like a save() method or update_record() method. Something like that), you could just append the
self.__modified = datetime.now().isoformat()
just before its all committed, since thats the only time it really matters anyway.

Categories