Git Product home page Git Product logo

betterpython's Introduction

betterpython's People

Contributors

cclauss avatar diego-bearing avatar ecancino avatar egges avatar trolzen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

betterpython's Issues

Undefined name 'Blog' in type hint

$ flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics

./betterpython/7 - dealing with errors/monadic-error-handling/example.py:22:35: F821 undefined name 'Blog'
def fetch_blog(blog_id) -> Result['Blog', Exception]:
                                  ^
./betterpython/7 - dealing with errors/monadic-error-handling/example.py:41:27: F821 undefined name 'Blog'
def blog_to_dict(item) -> 'Blog':
                          ^
./betterpython/7 - dealing with errors/monadic-error-handling/example.py:52:28: F821 undefined name 'Blog'
def verify_access(blog) -> 'Blog':
                           ^
3     F821 undefined name 'Blog'
3

Coupling alternative

Saw your posts on reddit and watched your first video, the one on coupling. Before watching the walk-through, I had a try at improving the "before" code alone.
I thought it could be fun to discuss that here, if you or someone else is up to it (btw, thanks for putting this up in a repo, super convenient!). Maybe it's silly.
In any case, it's hard to find the right kind of feedback on these kinds of exercises (when you're self-learning and it's not just Hello World with a SyntaxError anymore) ;). And maybe you get something out of it by seeing how viewers "interpret" your content.

import random
import string
from dataclasses import dataclass, field
from enum import Enum
from typing import Union


class VehicleRegistry:
    def __init__(self, id_length) -> None:
        self.id_length = id_length

    def random_id(self):
        length = self.id_length
        id = "".join(random.choices(string.ascii_uppercase, k=length))
        license = f"{id[:2]}-{''.join(random.choices(string.digits, k=2))}-{''.join(random.choices(string.ascii_uppercase, k=2))}"
        return id, license


class PropulsionTax(Enum):
    ELECTRIC = 0.02
    COMBUSTION = 0.05


@dataclass
class Vehicle:
    make: str
    model: str
    price: int
    tax_bracket: PropulsionTax

    # Include in class data (repr, ...), but do not init it, calculate it later:
    tax: float = field(init=False)

    # Straight from the factory, doesn't have any of these yet:
    id: Union[str, None] = None
    license: Union[str, None] = None

    def __post_init__(self):
        self.tax = self.price * self.tax_bracket.value


class Application:
    def __init__(self, vehicle: Vehicle):
        self.vehicle = vehicle
        self.registry = VehicleRegistry(12)

    def register_vehicle(self):
        self.vehicle.id, self.vehicle.license = self.registry.random_id()

        print("Registration complete. Vehicle information:")
        print(self.vehicle)


vehicle = Vehicle(
    make="Volkswagen",
    model="ID3",
    price=35000,
    tax_bracket=PropulsionTax.ELECTRIC,
)
app = Application(vehicle)
app.register_vehicle()

Comparing to your after code, there are some differences. There are a bunch of assumptions which might be incorrect.

  • Instead of implementing print methods, implement __str__ (or __repr__) and call built-in print on the objects. But wait...
  • When you drafted up your classes by putting type hints at the top, I thought you'd definitely convert them into dataclasses in a wild plot twist! That didn't end up happening, yet those bad boys are perfect for this use case. They have built-in representation for all their member variables, so the previous step is solved automatically.
  • Taxes are an Enum. Advantages:
    • Can be extended (hydrogen!) and used pretty arbitrarily
    • We get nice names/reprs, no hard-coded magic except in that one central place
  • License and ID generation are kind of the same thing. The license strictly depends on the generated ID. We therefore call both methods one after the other anyway, everytime. There's no instance where we call only one of each. So they can be the same thing, one method returning a tuple of ID and license plate.
  • I assumed VehicleRegistry was some kind of office that does... vehicle registrations. As such, it probably uses (again, an assumption) IDs of the same length anyway, for all vehicles. Hence, this is now a parameter to the constructor itself.
  • I thought you'd definitely hand in an Application for a vehicle. This makes sense. You hand in a string representing the vehicle, but we can just hand in the vehicle object itself. Makes it much more dynamic and less coupled, in my view.
  • Maybe the strangest thing to me was how the Application or, rather, its register_vehicle method creates the vehicle. I thought of the process more like real-life:
    1. Vehicle gets created in some car factory, it just exists for now (I just do vehicle = Vehicle(...) on the module level). At that point, it doesn't have a registration ID or license plate yet, but it comes with "empty slots" (defaults of None) for them. This abstraction works especially well for the license plate. Cars literally come with a blank space where that will go later, for which None is the perfect representation.
    2. Now, the consumer takes the car to the registry. They create an Application which has a (as opposed to is a/inheritance) vehicle. Makes sense, the application form contains the vehicle data sheet (the Vehicle dataclass instance object itself).
    3. The registry then only generates a new random ID/license plate for the new car. In the current state, it doesn't need to know/do anything else. This might change, at which point my shit breaks apart.
    4. We "take" the Application to the registry, where we simply slap on the new ID/license plate onto the new car, replacing None.

I think using dataclasses is a big improvement and nicely showcases the "batteries included" aspect of Python. Lots of boilerplate taken care of for us. Secondly, pulling out the "car creation" process entirely makes things much simpler, too, while being more in line with the metaphore, in my mind. This all leads to very short (cohesive?) functions/methods will quite low coupling.

Would be happy to hear your or someone else's thoughts on this! Loving the series so far, finally good content that fills the huge gap between Hello World (for which approx. 484092 guides exist) and actually working in the industry.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.