I am trying to use assert() to check the content of a simple class without the need to check each of the members of the class. Is something like the following possible?
def class class_data(object):
def __init__ (self, name = 'Richie', school = 'Jefferson High', age = 17):
self.name = name
self.school = school
self.age = age
myschool = class_data()
#check for correct data via assert
assert (myschool == class_data('Fonzie', 'Lincoln High', 17))
Please excuse me if my questions is nonsensical, and many thanks in advance.
You need to define the __eq__ method on your class.
When you do myschool == other, Python will call myschool.__eq__(other), which by default just whether checks both objects are the same (in your case, they aren't).
You can override __eq__ to achieve your purpose, here's an example:
def class class_data(object):
def __init__ (self, name = 'Richie', school = 'Jefferson High', age = 17):
self.name = name
self.school = school
self.age = age
def __eq__(self, other):
for attr in ("name", "school", "age"):
if getattr(self, attr) != getattr(other, attr):
return False
return True
Note: using getattr isn't necessarily the best thing to do here. The implementation you choose is up to you.
Related
Topic Closed
So I'm learning OOP in python and wanted to test my knowledge. That's what i did
class Student:
def cantBeStudent():
print('You don\' classify as a stududent')
def __init__(self, age, education):
self.age = age
self.education = education
if (self.age < 16) or (self.education < 3):
cantBeStudent()
student1 = Student(age=18, education=2)
I get name_error when i try to call cantBeStudent(). It says that cantBeStudent is not defined. I can't find my answer on google so I came here.
Edit: Also when i comment out whole cantBeStudent i get SyntaxError on def init
You need to add self to the method invocation and declaration:
class Student:
def cantBeStudent(self): # need self
print('You don\' classify as a stududent')
def __init__(self, age, education):
self.age = age
self.education = education
if (self.age < 16) or (self.education < 3):
self.cantBeStudent() # need self
student1 = Student(age=18, education=2)
OR
You need to invoke cantBeStudent as a static method like so:
class Student:
def cantBeStudent(): # no self as first argument, therefore static method
print('You don\' classify as a stududent')
def __init__(self, age, education):
self.age = age
self.education = education
if (self.age < 16) or (self.education < 3):
Student.cantBeStudent() # because declaration has no self,
# cantBeStudent belongs to entire Student class
student1 = Student(age=18, education=2)
When you construct a class, methods that you define must take the instance as the first argument. The class instance is referred to as self (though you could call it anything you wanted):
class X:
def __init__(self, number):
self.number = number
def add_number(self, arg):
self.number += arg
You see this when you define __init__. All other functions* work this way as well. When you call them like
instance = X(1)
instance.add_number(3)
It's analogous to doing:
instance = X(1)
X.add_number(instance, 3)
It's just calling the method against the instance will automatically pass self for you. When you call that method inside the instance, you need to specify the instance you are calling against, it's just this is called self instead of instance:
class X:
~snip~
def add_number(self, arg):
self.number += arg
def increment_number(self):
# note the self.<method>
self.add_number(1)
Again, this would be identical to the call:
instance = X(1)
X.increment_number(instance)
Because the instance gets passed in so that it can be called with the appropriate method
* All other functions that are not decorated with #staticmethod or #classmethod
You should provide self to any function that you want it to be counted as an object's method. If you do not want to provide self that function could be a static function (which means it does not rely on the type of the object itself). Then, you need to clarify that function, by #staticmethod decorator.
You missed self parameter on cantBeStudent method and when call it from contructor, it should be self.canBeStudent. Like this:
class Student:
def cantBeStudent(self):
print('You don\' classify as a stududent')
def __init__(self, age, education):
self.age = age
self.education = education
if (self.age < 16) or (self.education < 3):
self.cantBeStudent()
The purpose of self in Python: What is the purpose of the word 'self', in Python?
Add self on the function cantBeStudent
class Student:
def cantBeStudent(self):
print("You don't classify as a stududent")
def __init__(self, age, education):
self.age = age
self.education = education
if (self.age < 16) or (self.education < 3):
self.cantBeStudent()
That happens because you need to specify that the function is contained in the same class of 'self'
If you would done in this way it would had worked:
def cantBeStudent():
print("You don't classify as a stududent")
class Student:
def __init__(self, age, education):
self.age = age
self.education = education
if (self.age < 16) or (self.education < 3):
cantBeStudent()
The main difference is that in the first case, the function is inside the class Student, in the other case the function is out of the class, so don't need the 'self'
I'am using more class based programs, however in some cases it's not handy to provide all self.paramets into a class.
In those cases I want to use a regular input into a function in a class. I figured out a way to achieve both inputs, let me show this in following script:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def myfunc(a):
if (type(a) == str):
name = a
else:
name = a.name
print("Hello my name is " + name)
p1 = Person("John", 36)
p1.myfunc()
print("---------------------")
Person.myfunc("Harry")
Output:
Hello my name is John
---------------------
Hello my name is Harry
First, the name is initialized by the classes self.params.
Second, the name is provided in the method within the class as a string.
So a type check is necessary.
However I don't think this is a clean approach, because when I have >30 methods I need to implement these type checks again, including upcoming type-error results.
Does anyone know a better approach?
The simplest solution is to implement a __str__ method for your class. This method will be called whenever something tries to convert an instance of the class to a string.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return self.name
p = Person('Jane', 25)
print('Hello', p)
'Hello Jane'
I have multiple classes and I have instances from each class e.g: Student class. every instance (a student) has their own courses. Now when a user signs in (by input) I want to print their list of courses. Or even just their age to show that I have the correct object.
Is there a better way than eval() to get an object from class based on input
like the following example:
class Student:
def __init__(self, name, age):
self._name = name
self._age = age
blablue = Student('bla blue', '23')
name = input('enter your name')
name = name.split(' ')
stundent = eval(name[0] + name[1])
print(student)
print(student.age)
output:
enter your name: bla blue
<__main__.Foo object at 0x000001B2978C73C8>
23
I assume this is for educational purpose (production code would use a SQL database and some ORM):
try:
# python 2.x
input = raw_input
except NameError:
# python 3.x
pass
class AlreadyExists(ValueError):
pass
class DoesNotExist(LookupError):
pass
class FooCollection(object):
def __init__(self):
self._foos = {}
def add(self, foo):
if foo.name in self._foos:
raise AlreadyExists("Foo with name '{}' already exists".format(foo.name))
self.update(foo)
def update(self, foo):
self._foos[foo.name] = foo
def get(self, name):
try:
return self._foos[name]
except KeyError:
raise DoesNotExist("no Foo named '{}'".format(name))
class Foo(object):
def __init__(self, name, age):
self._name = name
self._age = age
# we at least need to be able to read the name
#property
def name(self):
return self._name
def __repr__(self):
return "Foo({}, {})".format(self._name, self._age)
def main():
foos = FooCollection()
blablue = Foo('bla blue', '23')
foos.add(blablue)
name = input('enter your name: ').strip()
try:
print("found {}".format(foos.get(name)))
except DoesNotExist as e:
print(e)
if ___name__ == "__main__":
main()
The principle here is to have a storage for your instances. I chose a dict for fast lookup with the Foo.name as key, in real life you'd probably want an opaque unique identifier for each instance and multiple indexes (i.e. one by id, one by name etc) - but actually in real life you would use a SQL database that already provide all those features in a much more optimized way ;-)
Also, I wrapped the dict in a dedicated class with its own interface. This allows to decouple the interface from the implementation (if you later decide you want more indexes than just name for example), and encapsulate domain logic too (i.e. checking you don't accidentally overwrite an existing Foo).
Many of my classes look like the following class to represent accounts
class Account(object):
def __init__(self, first, last, age, id, balance):
self.first = first
self.last = last
self.age = age
self.id = id
self.balance = balance
def _info(self):
return self.first, self.last, self.age, self.id, self.balance
def __eq__(self, other):
return self._info == other._info()
def __hash__(self):
return hash((type(self), self.info()))
def ... # other methods follow
But really the only relevant information is the list of attributes I care about first, last, age, id, balance. Is there a standard method to define Python classes that follow this structure?
At first glance I thought of namedtuple but I'm not sure that that allows me to add additional methods after the fact. Really, I want something like the following
class Account(object):
attributes = "first last age id balance"
def ... # other methods
What is the best way of obtaining this?
Not sure how idiomatic it is, but the following satisfies your requirements:
class Slottable:
def __init__(self, *args):
for slot, arg in zip(self.slots.split(' '), args):
setattr(self, slot, arg)
def _info(self):
return tuple(getattr(self, attr) for attr in self.slots.split())
def __eq__(self, other):
return self._info() == other._info()
def __hash__(self):
return hash((type(self), self._info()))
class Account(Slottable):
slots = "first last age id balance"
def fullname(self):
return self.first + " " + self.last
matt = Account("Matthew", "Smith", 28, 666, 1E6)
john = Account("John", "Jones", 46, 667, 1E7)
d = {matt: 5, john: 6} # Hashable
print matt.fullname()
#=> "Matthew Smith"
print john.fullname()
#=> "John Jones"
print matt == matt, matt == john
#=> True False
matt.age = 29 # Happy birthday!
print matt.age
#=> 29
Here are some recipes you can try: override __setattr__, __dict__, __slots__ and/or init. Let us know what works for you.
Many libraries out there exist to cover this need: attrs, dataclasses, pydantic, ... and my new addition to this landscape, pyfields.
Choice will mainly depend on the features you need or do not need. pyfields focuses on fields definition and optional validation and conversion, without any constraint on your class. Fields that can be native become as fast as python native attributes can be, while fields requiring callbacks (validators/converters) are implemented using descriptors.
You can blend your own constructor with the
from pyfields import field, init_fields
class Account(object):
first = field(doc="first name")
last = field(doc="last name")
age = field(doc="the age in years")
id = field(doc="an identifier")
balance = field(doc="current balance in euros")
#init_fields
def __init__(self, msg):
print(msg)
a = Account("hello, world!", first="s", last="marie", age=135, id=0, balance=-200000)
print(vars(a))
yields
hello, world!
{'balance': -200000, 'id': 0, 'age': 135, 'last': 'marie', 'first': 's'}
As opposed to other, more "all in one" libraries, pyfields concentrates on the fields and the constructor only, with a "minimum viable product" spirit. So if you would also like dict representation and conversion, hash, equality and comparison, you should add them on top of your class using another library. I am currently developing a mixture lib providing mix-in classes for this, with the same philosophy of a la carte features - that you will be able to use with or without pyfields.
See pyfields documentation for details. Do not hesitate to provide feedback !
I have a Person class like this:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return '<Person {}>'.format(self.name)
I want to add some instances of this class to a set, like this:
tom = Person('tom', 18)
mary = Person('mary', 22)
mary2 = Person('mary2', 22)
person_set = {tom, mary, mary2}
print(person_set)
# output: {<Person tom>, <Person mary>, <Person mary2>}
As you can see, there are 2 Marys in the set. How can I make it so that Person instances with the same age are considered the same person, and only added to the set once?
In other words, how can I get a result of {<Person tom>, <Person mary>}?
When a new object is being added to a python set, the hash code of the object is first computed and then, if one or more objects with the same hash code is/are already in the set, these objects are tested for equality with the new object.
The upshot of this is that you need to implement the __hash__(...) and __eq__(...) methods on your class. For example:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
return self.age == other.age
def __hash__(self):
return hash(self.age)
def __repr__(self):
return '<Person {}>'.format(self.name)
tom = Person('tom', 18)
mary = Person('mary', 22)
mary2 = Person('mary2', 22)
person_set = {tom, mary, mary2}
print(person_set)
# output: {<Person tom>, <Person mary>}
However, you should think very carefully about what the correct implementation of __hash__ and __eq__ should be for your class. The above example works, but is non-sensical (e.g. in that both __hash__ and __eq__ are defined only in terms of age).