#๐Ÿ”’ Accessing JSON key with space in it

130 messages ยท Page 1 of 1 (latest)

rocky warren
#

Hey there, got a quick one:

How would I be able to search for the json_object "Licence Type", when they key has a space in it?
Python:

class Customer:
    def __init__(self, Name, Address, DOB, Licence Type):
        self.name = Name
        self.address = Address
        self.dob = DOB
        self.licence_type = Licence Type
#...
        with open("Customers.json", "r") as fr:
            customersFile = json.load(fr)
            customersList = []
            for json_object in customersFile:
                customer = Customer(**json_object)
                customersList.append(customer)

JSON:

[
  {
    "Name": "Jimmy Jones",
    "Address": "123 Any Street Kilmarnock",
    "DOB": "15/12/1975",
    "Licence Type": "Manual"
  },
rapid pumiceBOT
#

@rocky warren

Python help channel opened

Remember to:

  • Ask your Python question, not if you can ask or if there's an expert who can help.
  • Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
  • Explain what you expect to happen and what actually happens.

:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.

copper smelt
drowsy elm
#

Use **kwargs

rocky warren
drowsy elm
#

or just pass the raw dict object to Customer()

rocky warren
drowsy elm
#

!kwargs

rapid pumiceBOT
#
The `*args` and `**kwargs` parameters

These special parameters allow functions to take arbitrary amounts of positional and keyword arguments. The names args and kwargs are purely convention, and could be named any other valid variable name. The special functionality comes from the single and double asterisks (*). If both are used in a function signature, *args must appear before **kwargs.

Single asterisk
*args will ingest an arbitrary amount of positional arguments, and store it in a tuple. If there are parameters after *args in the parameter list with no default value, they will become required keyword arguments by default.

Double asterisk
**kwargs will ingest an arbitrary amount of keyword arguments, and store it in a dictionary. There can be no additional parameters after **kwargs in the parameter list.

Use cases

  • Decorators (see /tag decorators)
  • Inheritance (overriding methods)
  • Future proofing (in the case of the first two bullet points, if the parameters change, your code won't break)
  • Flexibility (writing functions that behave like dict() or print())

See /tag positional-keyword for information about positional and keyword arguments

copper smelt
rocky warren
#

will try that 1 sec

bleak wave
#

I don't suggest to switch your init to use kwargs

rocky warren
bleak wave
#

instead I would make a classmethod like from_json or something

rocky warren
drowsy elm
#

attrs.field supports aliases

bleak wave
#
from operator import itemgetter


class Customer:
    def __init__(
        self,
        name,
        address,
        dob,
        licence_type,
    ):
        self.name = name
        self.address = address
        self.dob = dob
        self.licence_type = licence_type

    @classmethod
    def from_json(cls, d):
        return cls(
            *itemgetter(
                "Name",
                "Address",
                "DOB",
                "Licence Type",
            )(d)
        )


with open("Customers.json") as f:
    customers_as_dict = json.load(f)

customers = [Customer.from_json(d) for d in customers_as_dict]
rocky warren
#

oof

#

probs a good solution but dont think my lecturer would be happy importing that

drowsy elm
#

Then do it all manually.

rocky warren
#

"beginners course to python" this is meant to be haha

drowsy elm
#
Customer(name=d['Name'], address=d['Address'], ...)```
bleak wave
#

well, cls(...) as it is a classmethod

drowsy elm
#

or without the classmethod

bleak wave
#
@classmethod
def from_json(cls, d):
    return cls(
        d["Name"],
        d["Address"],
        d["DOB"],
        d["Licence Type"],
    )
#

actually looks cleaner than what I did, I was just feeling funky

drowsy elm
#

Would using a TypedDict be allowed?

bleak wave
#

TypedDict I don't think can have spaces in the keys?

#

maybe the functional syntax, but I feel like maybe that's deprecated, idk

#

!d typing.TypedDict

rapid pumiceBOT
#

class typing.TypedDict(dict)```
Special construct to add type hints to a dictionary. At runtime it is a plain [`dict`](https://docs.python.org/3/library/stdtypes.html#dict).

`TypedDict` declares a dictionary type that expects all of its instances to have a certain set of keys, where each key is associated with a value of a consistent type. This expectation is not checked at runtime but is only enforced by type checkers. Usage...
drowsy elm
bleak wave
#

oh I see, it's the kwarg syntax that is deprecated

drowsy elm
#
from typing import TypedDict

Customer = TypedDict("Customer", {
  "Name": str,
  "Address": str,
  "DOB": str,
  "License Type": str
})```
bleak wave
#

uh

#

that clashes with the Customer class tho

rocky warren
#

brb bus im on just broke doon lol

drowsy elm
#

It replaces the Customer class since it doesn't do anything fancy. It's just a not fancy dataclass

rocky warren
#

great timing

bleak wave
#

TypedDict is not a dataclass at all, it returns a dict

drowsy elm
#

but it acts like one

bleak wave
#

no it doesn't

drowsy elm
#

It only defines an init method that assigns its arguments to attributes

bleak wave
#

it does not do that, it returns a dict

#

!e

from typing import TypedDict

Point = TypedDict("Point", {
    "x": int,
    "y": int
})

p = Point(**{"x": 1, "y": 2})
print(p, type(p))
drowsy elm
#

gotta unpack the dict

#

or not

rapid pumiceBOT
#

@bleak wave :white_check_mark: Your 3.12 eval job has completed with return code 0.

{'x': 1, 'y': 2} <class 'dict'>
bleak wave
#

same thing

drowsy elm
#

I know TypedDict is a dict at runtime

#

I don't see why that would matter.

#

I'm just gonna assume he can't use typing

bleak wave
#

and that's all the TypedDict is, more precise typing for dicts

#

in that case OP would just use a bare dict as it's clear they aren't using typehints

drowsy elm
#

agreed

bleak wave
#

@rocky warren ```py
class Customer:
def init(
self,
name,
address,
dob,
licence_type,
):
self.name = name
self.address = address
self.dob = dob
self.licence_type = licence_type

@classmethod
def from_json(cls, d):
    return cls(
        d["Name"],
        d["Address"],
        d["DOB"],
        d["Licence Type"],
    )

with open("Customers.json") as f:
customers_as_dict = json.load(f)

customers = []
for d in customers_as_dict:
customers.append(Customer.from_json(d))

something like this then
#

no fancy stuff

#

you have the normal init, then you have a factory function for constructing a Customer from the json dicts

rocky warren
#

im back now stranded in the middle of nowhere lol

rocky warren
bleak wave
#

the bus may not go on, but the pyth will go on ๐Ÿ˜„

bleak wave
#

you can see the @classmethod above it. That marks it as a method on the class, not on an instance

rocky warren
#

ahhhh gotcha

#

will try it oot now

bleak wave
#

also notice how I removed all the stuff that doesn't need the file from the with statement

#

you should exit the with as soon as you can, so that you aren't keeping a file open for no reason

rocky warren
#

aye see that

#

looks like it works!

#

will just double check what it looks like with the whole json

bleak wave
rocky warren
#

to my understanding, "from_json" is a dictionary, which allows me to have a space in a key
i then loop this dictionary to find all the keys in the json, while keeping the other keys for the Customer class

bleak wave
#

uh

#

from_json is a classmethod of the Customer class, which takes a dictionary and builds a Customer out of it

rocky warren
#

wdym by "takes a dictionary"?

bleak wave
#

it's a method

#

you pass a dictionary to it

#
def f(a, b):
    print(a * b + b * a)

you could say that f is a function which takes two numbers

rocky warren
#

gotcha

#

what would be the line to get, say all the customers addresses?

#

or cthe customers address in index 2?

bleak wave
#

there are no indices in a dict

bleak wave
rocky warren
#

aye

bleak wave
#
customers[2].address
rocky warren
#

ahhhhhhhhh ffs i was doing

customers.address[2]
#

alright aye im gettin it

bleak wave
#

smh

rocky warren
#

the way indexes work in that case would be fine in loops as well?

bleak wave
#

idk what you mean

#
for customer in customers:
    print(customer.address)
#

customers is just a list

#

you can do everything with a list that you can do with a list

rocky warren
#

for context:

#

theres like bajillion "for x in range(0, len(something))" loops later on

#

looping through the object would work the same way as looping through an array?

drowsy elm
rocky warren
#

*lists

drowsy elm
#

!d array.array

bleak wave
drowsy elm
#

if you want to be technical...

bleak wave
#

I guess there is bytearray, but it's not a generic array

rocky warren
bleak wave
rocky warren
#

is it inefficient?

drowsy elm
#

it's overly verbose.

bleak wave
#

it's super verbose and yes, less efficient as well

drowsy elm
#
for x in something:
  print(x.address)
bleak wave
#

it's much better to have a descriptive name of what each iteration is

bleak wave
rocky warren
#

alright thats good to know, will go for it like that in OOP projects from now
for the sake of time in this project i probably have to stick to my shite way, since its kinda everywhere already lol
and also my lecturer said "efficiency doesnae fuckin matter"

#

i know hes crazy

drowsy elm
#

it doesn't matter for beginners.

#

But this isn't just about efficiency.

#

After all, the first rule of optimization is "Don't"

bleak wave
#

and it's mostly not about efficiency, it's about readability

drowsy elm
#

Do we write functions because it's more efficient to?

bleak wave
#
for customer in customers:
    print(customer.address)

VS

for i in range(len(customers)):
    print(customers[i].address)
drowsy elm
#

it's more concise

rocky warren
#

aye the comparison is good cheers

#

i think thats all the questions ive got for now

#

thanks again lads

#

absolute legendssssssss

rapid pumiceBOT
#
Python help channel closed

This help channel has been closed and it's no longer possible to send messages here. If your question wasn't answered, feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.