Calling Classes With Atributes

Which a efficient/best way:
module.class.method().attribute.
or
module.class().method().attribute
or
module.class.method.attribute
or
module.class().method.attribute
Purpose: To get a attribute from another module from a class from a method call attribute created by that.
(someone, @iontan)?
I’m not that sure.

If you’re getting objects from another module you’d be using import or from in the first place. Neither of which allow for your examples, you’d likely first import the class (or the entire module) in your top-level module and then perform any additional steps (dependent on what exactly you’re after).

As for attributes of methods I’m not 100% what you mean. Does the method return a specific object that has an attribute? Do you actually want an attribute bound to a function object or the result of calling it? Should this be a method of an instantiated class or are you making use of the original bound definition?

An example here would be useful.

On a side note I don’t think everyone appreciates being tagged in every other question, please limit your usage unless it is of extreme relevance to a single user (and even then linking relevant forum threads is probably a nicer thing to do).

I mean that there is a different module(which is actually a file in the project). There is a class from the module. I want to check a attribute from a method which is already called.
Do I use this:
module.class().attribute
or
module.class.attribute?
Edit:
Use for this:
I want to check the attribute against True with unittest.assertTrue method in a class already defined. Here is the project:
https://www.codecademy.com/courses/learn-intermediate-python-3/projects/int-python-sams-surf-shop.
Reason for asking:
Code failed:

  #@unittest.expectedFailure
  def test_apply_locals_discount(self):
    surfshop.ShoppingCart.apply_locals_discount()
    self.assertTrue(surfshop.ShoppingCart.locals_discount)

.
An error occured:

.E
======================================================================
ERROR: test_apply_locals_discount (__main__.Tests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests.py", line 18, in test_apply_locals_discount
    surfshop.ShoppingCart.apply_locals_discount()
TypeError: apply_locals_discount() missing 1 required positional argument: 'self'

----------------------------------------------------------------------
Ran 2 tests in 0.000s

FAILED (errors=1)

.
All code:
test.py:

# Write your code below:
import surfshop
import unittest

class Tests(unittest.TestCase):
  def setUp(self):
    self.cart = surfshop.ShoppingCart()

  @unittest.skip
  def test_add_surfboards(self, num):
    for n in [2, 3, 4]:
      with unittest.subTest(num):
        self.assertEqual("Successfully added {num} surfboard to cart!", surfshop.ShoppingCart().add_surfboards(num))
        self.assertRaises(surfshop.TooManyBoardsError, surfshop.ShoppingCart().add_surfboards(num))

  #@unittest.expectedFailure
  def test_apply_locals_discount(self):
    surfshop.ShoppingCart.apply_locals_discount()
    self.assertTrue(surfshop.ShoppingCart.locals_discount)

unittest.main()

surfshop.py:

import datetime

class TooManyBoardsError(Exception):
    pass

class CheckoutDateError(Exception):
    pass

class ShoppingCart:
    def __init__(self):
        self.num_surfboards = 0
        self.checkout_date = None
        self.locals_discount = False

    def add_surfboards(self, quantity=1):
        if self.num_surfboards + quantity > 4:
            raise TooManyBoardsError("Cart cannot have more than 4 surfboards in it!")
        else:
            self.num_surfboards += quantity
            suffix = '' if quantity == 1 else 's'
            return f'Successfully added {quantity} surfboard{suffix} to cart!'

    def set_checkout_date(self, date):
        if date <= datetime.datetime.now():
            raise CheckoutDateError
        else:
            self.checkout_date = date

    def apply_locals_discount(self):
        self.locals_discount = True

solution_test.py:

import unittest
import surfshop
import datetime

class SurfShopTests(unittest.TestCase):

    def setUp(self):
        self.cart = surfshop.ShoppingCart()

    def test_add_surfboard(self):
        message = self.cart.add_surfboards(quantity=1)
        self.assertEqual(message, f'Successfully added 1 surfboard to cart!')

    def test_add_surfboards(self):
        for i in range(2, 5):
            with self.subTest(i=i):
                message = self.cart.add_surfboards(i)
                self.assertEqual(message, f'Successfully added {i} surfboards to cart!')
                self.cart = surfshop.ShoppingCart()

    # old version without parameterization
    # def test_add_surfboards(self):
    #     message = self.cart.add_surfboards(2)
    #     self.assertEqual(message, f'Successfully added {i} surfboards to cart!')
    #     self.cart = surfshop.ShoppingCart()

    @unittest.skip
    def test_add_too_many_surfboards(self):
        self.assertRaises(surfshop.TooManyBoardsError, self.cart.add_surfboards, 5)

    # commented out - test should not fail at the end of project
    # @unittest.expectedFailure
    def test_apply_locals_discount(self):
        self.cart.apply_locals_discount()
        self.assertTrue(self.cart.locals_discount)

    def test_add_invalid_checkout_date(self):
        date = datetime.datetime.now()
        self.assertRaises(surfshop.CheckoutDateError, self.cart.set_checkout_date, date)


unittest.main()

solution_surf.py:

import datetime

class TooManyBoardsError(Exception):
    def __str__(self):
        msg = 'Cart cannot have more than 4 surfboards in it!'
        return msg

class CheckoutDateError(Exception):
    pass

class ShoppingCart:
    def __init__(self):
        self.num_surfboards = 0
        self.checkout_date = None
        self.rental_days = None
        self.locals_discount = False

    def add_surfboards(self, quantity=1):
        if self.num_surfboards + quantity > 4:
            raise TooManyBoardsError
        else:
            self.num_surfboards += quantity
            suffix = '' if quantity == 1 else 's'
            return f'Successfully added {quantity} surfboard{suffix} to cart!'

    def set_checkout_date(self, date):
        if date <= datetime.datetime.now():
            raise CheckoutDateError
        else:
            self.checkout_date = date

    def apply_locals_discount(self):
        self.locals_discount = True

.

You’ve even added in the solution. As you can see there’s a difference when calling functions directly bound to a class and when calling methods from an instance.

class Test:
    # provided as example only
    def func(x):
        print(x)

Test.func(3)
Out: 3
# whereas the following would throw an error as `self` is passed first
# as well as integer 3
Test().func(3)
Out: TypeError: func() takes 1 positional argument but 2 were given

Your example is the inverse, you’re calling the bound function directly but the function expects a single positional argument self to be passed. You’re not using a method object so there is no instance passed as the first argument by default. You could then pass an instance object but I doubt that’s the intention.

You can learn a little more about how this works at: https://docs.python.org/3/tutorial/classes.html#method-objects

Double check your instructions when setting up your tests. It’s very likely you should be working with an instance here, so you most probably want to create an instance from your class first and perform your tests on that.

Why is self not one of the arguments; only x?
Also I still confused why this:

is different to this:

?

Arguments or parameters? If parameters you can bind functions to classes without self, they are first-class objects after all. It’s generally a bad idea of course since they’ll likely throw errors when called from instances (ignoring any staticmethod decorators for now) but it is included to make the point about the difference with method objects.

If arguments, how could there be a self passed? There’s no instance in the first part of that example (only at the very end do we call the class and create one). Remember how class instantiation works, classname is different to actually calling it classname(). The given link explains the difference when calling methods from instances and self being passed as an argument.

I think your second query is the same query about instances vs. classes again. Might be worth revisiting anything you’ve covered on classes just to make sure you’ve not missed something.

It didn’t really explain this.
Normally it’s this:

instead of this:

Is there any article you can share with me to understand this better because I don’t?

For a class? It is not normally that, that’s an uncommon usage. Most of the time you create instances from your class and make use of them. As a very quick example-

class Dog:
    
    def __init__(self, name):
        self.name = name

    def talk(self):
        return f"{self.name} barks!"


dog1 = Dog("Spartacus")
dog2 = Dog("Ruffles")
print(dog1.talk())
print(f"Our second dog is called {dog2.name}")

Out:
Spartacus barks!
Our second dog is called Ruffles

Using it like Test().func(3) is arguably uncommon but that’s because you normally want to assign a reference to the created instance somewhere so it isn’t garbage collected, I’m trying to highlight the difference between the class itself and instances you create from it.

I really think you’ve missed something when learning about classes that will likely hold you back if you don’t understand it. I think you’d want to revisit the section on classes.

There’s lots of sections on classes. In this course there is a OOP one and on the basic courses there is a Classes one.

From what you’ve described revisiting the basics might be a good idea. Instantiation from classes is essential to know, it’s their main purpose.

I wondered what if I use () in the class calling when getting attributes from classes.
class().attribute This instantiates a class, does it?
class.attribute

Also, if you have 2 files/modules: m1.py, m2.py, can you import m2.py into the m1.py file and change m2.py with this syntax:
m1.py:

import m2
m2.variable2 = 2

m2.py:

variable1 = 1

Is that acceptable?

Creating objects from classes has been covered, if you have a new query please add it as a new question thread.

1 Like

I mean getting attributes from classes with the () next to the class name hasn’t been explained in the course content.

How do I make a new question thread? Is a thread a topic?

Let’s stay on topic; your post has been flagged by the community but I didn’t flag it.

Hi,

Can I ask why do we need to include “self.cart = surfshop.ShoppingCart()” after “self.assertEqual(message, f’Successfully added {i} surfboards to cart!’)” when we already called the function and assigned the values with “message = self.cart.add_surfboards(i)”

Thanks a bunch!

see here