I want to make Books/Customer/Loan Classes and make all my function to be part of the class, but instead I only make it worse (I think lol).
I know that my code needs a lot of correction (Make the code more readable) but still maybe some Hero here could help and save my day (Make me learn something new along the way)
P.S: I'm learning how to make readable code so be gentle LOL.
Example of Customer Class of my code:
class Customer:
"""
A class that represents the Customer object
"""
def __init__(self, customer_id, customer_name, customer_city, customer_age):
"""
A function that contains all the relevant information of customers
:param customer_id: Customer's ID
:param customer_name: Customer's name
:param customer_city: Customer's city of living
:param customer_age: Customer's age'
"""
self.customer_id = customer_id
self.customer_name = customer_name
self.customer_city = customer_city
self.customer_age = customer_age
def __str__(self):
return f"{self.customer_id},{self.customer_name},{self.customer_city},{self.customer_age}"
Example of function that I want to make it part of the Customer Class instead of using it as a regular Function:
def add_new_customer(customer_id, customer_name, customer_city, customer_age):
"""
A function that add new customer to the Library
:param customer_id: Customer's ID'
:param customer_name: Customer's name'
:param customer_city: Customer's city'
:param customer_age: Customer's age'
"""
# todo: try different method that can return dict. return customers_library["Customers"].append({"Customer's ID":customer_id,"Customer's Name":customer_name,"Customer's City":customer_city,"Customer's age":customer_age})
new_customer = customers_library["Customers"].append(
{"Customer's ID": customer_id, "Customer's Name": customer_name, "Customer's City": customer_city,
"Customer's age": customer_age})
with open('customers_data.pkl', 'wb') as customer_save:
pickle.dump(customers_library, customer_save)
return new_customer
First of all, add_new_customer shouldn't be part of Customer class. I would rather see it as a method of Library which could contains collection of all customers. But to make it class method you just need to put it inside class, remember about identation and instance of the class (self) as first parameter.
Another hint and good practice is to not duplicate names - instead of customer.customer_name just write customer.name. In add_customer function you already now you adding customer, so it can take just name, city and age.
id is an exception as it would shadow builting id function and it's really common in database so in that one case it's good to have customer_id.
Another hint, you could remove a lot code with dataclasses
class Customer:
"""
A class that represents the Customer object
"""
from dataclasses import dataclass
#dataclass
class Customer
customer_id: int
name: str
city: str
age: str
And if we go further, there are possibility to put alias on field with dataclass-json or with pydantic, look:
from pydantic import BaseModel, Field
class Customer(BaseModel):
customer_id: int = Field(alias="Customer's ID")
class Config:
allow_population_by_field_name = True
c = Customer(customer_id=10)
print(c.dict(by_alias=True)) # prints {"Customer's ID": 10}
Which will simplify customer adding a lot (actually you can pickle pydantic model directly but it's one more way to go).
Related
Consider the following python code:
from dataclasses import dataclass
#dataclass
class Registration:
category: str = 'new'
#dataclass
class Car:
make: str = None
category: str = None
reg: Registration = None
def __post_init__(self):
''' fill in any missing fields from the registration of car '''
if self.reg:
for var in vars(self.reg):
if not self.var:
self.var = self.reg.var
r = Registration()
a = Car(make='ford', category='used', reg=r)
# its unknown if b is used/new, so we explicitly pass it None
b = Car(make='ford', category=None, reg=r)
In above example, the __post_init__ is supposed to fill in fields in Car class if it was not passed in during creation of Car object. However if None was explicitly passed in as the field value (in this case for category) it's not supposed to overwrite it from the Registration object. But the above code does. How do I detect what values were explicitly passed in during the object creation vs what are defaults?
I'd be surprised if there were a way to distinguish between
a None passed explicitly vs one that the object acquired via
its defaults. In situations like yours, one technique is to use
a kind sentinel value as the default.
#dataclass
class Car:
NO_ARG = object()
make: str = None
category: str = NO_ARG
reg: Registration = None
def __post_init__(self):
if self.reg:
for var in vars(self.reg):
if getattr(self, var) is self.NO_ARG:
setattr(self, var, getattr(self.reg, var))
However, you might also take the awkward situation you find yourself
in as a signal that perhaps there's a better way to model your
objects. Without knowing more about the
broader context it's difficult to offer definitive advice, but
I would say that your current strategy strikes me as fishy, so I
would encourage you to thinks some more about your OO plan.
To give one example of an alternative model, rather than using the Registration to
overwrite the attributes of a Car, you could instead build a property
to expose the Registration attribute when the Car attribute
is missing. A user of the class can decide whether they want
the category strictly from the Car or they are happy to take
the fallback value from the Registration, if available. This approach
comes with tradeoffs as well.
#dataclass
class Car:
make: str = None
category: str = None
reg: Registration = None
#property
def category_reg(self):
if self.category is None and self.reg:
return self.reg.category
else:
return self.category
i'm a little bit confuses about using graphene.
I am using the example of mutations on https://www.howtographql.com/graphql-python/3-mutations/ , but here only the example is shown how to create ONE link. Now it is more realistic for me that you have a list of links or other objects that you pass to your backend and later database. Is there anyone who has already implemented such an example?
I have taken a different example from https://docs.graphene-python.org/en/latest/types/mutations/#inputfields-and-inputobjecttypes . Below code snippet should help you in creating multiple instances in a single mutation.
import graphene
from .models import Person
class PersonInput(graphene.InputObjectType):
name = graphene.String(required=True)
age = graphene.Int(required=True)
class PersonType(DjangoObjectType):
class Meta:
model = Person
class CreatePerson(graphene.Mutation):
class Arguments:
person_objects = graphene.List(PersonInput, required=True)
persons = graphene.List(PersonType)
def mutate(root, info, person_objects):
persons = list()
for person_data in person_objects:
person = Person.objects.create(
name=person_data.name,
age=person_data.age
)
persons.append(person)
return CreatePerson(persons=persons)
mutation:
createPerson(personObjects: [{name: "Danish Wani" age:28}, {name: "Wani Danish" age:29}]){
persons{
name
age
}
}
My intention
So, I am developing an API package for one service. I want to make good typehints for every method, which exists in my library
For example, when user types get()., after the dot pycharm will let him know, what response this method will provide.
e.g:
info = get()
info. # and here IDE help with hints.
Pitfalls
But, there are some methods, which provide different responses depending of parameters in methods.
e.g.
# this method responses with object, containing fields:
# count - count of items
# items - list of ids of users
info = get()
# but this method will give additional information. It responses with object, containing fields:
# count - count of items
# items - list of objects with users' information. It has fields:
# id - id of user
# firstname - firstname of user
# lastname - lastname of user
# ... and some others
info = get(fields='firstname')
Objects structure
Now I have such structure (i't simplified)
from typing import List, Union
from pydantic import BaseModel, Field
class UserInfo(BaseModel):
id: int = Field(...)
firstname: str = Field(None)
lastname: str = Field(None)
some_other_fields: str = Field(None)
class GetResponseNoFields(BaseModel):
count: int = Field(...)
items: List[int] = Field(...)
class GetResponseWithFields(BaseModel):
count: int = Field(...)
items: List[UserInfo] = Field(...)
class GetResponseModel(BaseModel):
response: Union[GetResponseNoFields, GetResponseWithFields] = Field(...)
def get(fields=None) -> GetResponseModel:
# some code
pass
The problem
The problem is, when I type get(fields='firsttname').response.items[0]. pycharm shows me typehints only for int. He doesn't think, that items can contain List[UserInfo], he thinks, it only can have List[int]
I have tried
I've tried to use typing.overload decorator, but method 'get' has many parameters, and actually doesn't support default parameter values. Or maybe i didn't do it properly
Here what I have tried with overload (simlified). It didn't work because of 'some_other_param', but I leave it here just in case:
from typing import overload
#overload
def get(fields: None) -> GetResponseNoFields: ...
#overload
def get(fields: str) -> GetResponseWithFields: ...
def get(some_other_param=None, fields=None):
# code here
pass
When I try to call method without parameters, pycharm says, that "Some of the parameters is unfilled"
As per the GraphQL spec https://graphql.github.io/graphql-spec/draft/#sec-Input-Objects
the input object literal or unordered map must not contain any entries with names not defined by a field of this input object type, otherwise an error must be thrown.
Let's say I have a project which has a user called buyer, the schema for which is as follows
type Buyer {
id: ID!
name: String!
address: String!
email: String!
}
Now I can write a graphene schema for it
class BuyerType(DjangoObjectType):
class Meta:
model = Buyer
and make a mutation for this
class BuyerInput(graphene.InputObjectType):
name = graphene.String(required=False, default_value='')
address = graphene.String(required=False, default_value='')
email = graphene.String(required=False, default_value='')
class BuyerMutation(graphene.Mutation):
"""
API to create applicant
"""
class Arguments:
buyer_data = BuyerInput(required=True)
buyer = graphene.Field(BuyerType)
ok = graphene.Boolean()
def mutate(self, info, buyer_data=None):
buyer = Buyer(name=buyer.name, address=buyer.address, email=buyer.email)
buyer.save()
return BuyerMutation(buyer=buyer, ok=True)
and write the resolvers functions and query and mutation class. Pretty basic stuff so far.
And now to create a new buyer, I just call the mutation
mutation {
createBuyer(
name: "New Buyer"
address: "Earth"
email: "abc#example.com"
) {
ok
}
}
But if I pass an additional field called phone
mutation {
createBuyer(
name: "New Buyer"
address: "Earth"
email: "abc#example.com"
phone: 8541345474
) {
ok
}
}
an Unknown field 'phone' error comes up, which is understood.
But I want to make process for frontend devs easier. Rather than mentioning each field in the argument of mutation, they can just pass a dict which contains these three fields and also other arguments that might originate from a form submission, and then read the dict in the backend and only extract the fields that I need, much like it is done with REST APIs.
If this is possible, then how can I implement this?
We could get the class attributes of BuyerInput using the inspect module, and then assemble the arguments dictionary so that it ignores the keys which are not such an attribute. Say, we have the dict containing the parameters of createBuyer stored in the variable kwargs, this could look like the following:
import inspect
members = inspect.getmembers(BuyerInput, lambda a: not(inspect.isroutine(a)))
attributes = [
tup[0]
for tup in members
if not tup[0].startswith("_")
]
kwargs = {
key: value
for key, value in kwargs.items()
if key in attributes
}
Suppose I have two classes Employee and Student:
class Employee():
def __init__(self, id):
self.id = id # the employee id
...methods omitted...
class Student():
def __init__(self, id):
self.id = id # the student id, different from employee id
...methods omitted...
Now I'd like to create a third class StudentEmployee which simply merges Employee and Student.
However, the goal is that both id are still kept in each inherited class.
Some thing like this:
class StudentEmployee(Employee, Student):
def __init__(self, employee_id, student_id):
Employee.__init__(self, employee_id)
Student.__init__(self, student_id) # overrides employee id
Note that both Student and Employee have the id attribute so in reality one will override the other.
The question:
How can I keep both id as they carry different meaning?
For example, is there some way to protect id from one class from being over-riden by another class.
approach 1
One natural way is to change the class definition to:
class Employee():
def __init__(self, id):
self.eid = id # now "id" changes to "eid"
...attributes names in methods updated as well
class Student():
def __init__(self, id):
self.sid = id # now "id" changes to "sid"
...attributes names in methods updated as well
However, I don't like this approach very much because eid is not as neat as sid.
Moreover, the above example might be too simplistic.
Let's imagine the two classes being "merged" have many shared attribute name, the code refactoring work won't be small.
Any other better ways?