In Python, and many other languages, there is a value that means "no value". In Python, that value is None. So you could do something like this:

class User:
   username = None
   password = None

Those sure sound like instance variables though, and not class variables, so maybe do this:

class User(object):
    def __init__(self):
        self.username = None
        self.password = None

Note how Python assigns the None value implicitly from time to time:

def f():
    pass
g = f() # g now has the value of None
Answer from Kenan Banks on Stack Overflow
🌐
GeeksforGeeks
geeksforgeeks.org › python › how-to-create-an-empty-class-in-python
How to create an empty class in Python? - GeeksforGeeks
July 12, 2025 - # Python program to demonstrate # empty class class Geeks: pass # Driver's code obj = Geeks() print(obj) Output: <__main__.Geeks object at 0x02B4A340> Python also allows us to set the attributes of an object of an empty class. We can also set different attributes for different objects.
Discussions

Can you create a class with an empty list attribute
what should I do to work around this? When you initialize the object, set the attribute to an empty list. More on reddit.com
🌐 r/learnpython
7
1
February 22, 2023
python - Setting default/empty attributes for user classes in __init__ - Stack Overflow
When I am creating a new class, should I set all instance attributes in __init__, even if they are None and in fact later assigned values in class methods? See example below for the attribute resul... More on stackoverflow.com
🌐 stackoverflow.com
Given an object in python, can I somehow leave one attribute empty? - Stack Overflow
let's say, for some reason, i want to create an instance of class test, that only has the first attribute, leaving the second as null oder whatever. Is there any possibility to do so without creating a different class? More on stackoverflow.com
🌐 stackoverflow.com
python 3.x - Class attribute returning Empty - Stack Overflow
I created a class and initialized the attribute in the init method. I am updating the class attribute in the methods. After I created the object and trying the print the class attribute, it is retu... More on stackoverflow.com
🌐 stackoverflow.com
🌐
Readthedocs
jfine-python-classes.readthedocs.io › en › latest › construct.html
Constructing classes — Objects and classes in Python tutorial
Like most Python objects, our empty class has a dictionary. The dictionary holds the attributes of the object.
🌐
Reddit
reddit.com › r/learnpython › can you create a class with an empty list attribute
r/learnpython on Reddit: Can you create a class with an empty list attribute
February 22, 2023 -

I'm trying to create texas holdem with players being class instances with hand as an attribute of type list. When I instantiate a class with no value for hand it says I need instantiate it with a list, and when I try to use self.hand = None it doesn't allow me to append new generated cards to it since it's value type none, what should I do to work around this?

Edit: Now that I'm home I can add my code for people to see

Main Code

from pokerMethods import *
from playerClass import *

def main():
    deckNumber = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]
    deckSuit = ["Spades", "Diamonds", "Clubs", "Hearts"]
    cardList = []
    playerList = []
    p1 = Player("Herp", "Derp")
    p2 = Player("Herpy", "Derpy")

    createCard(p1, deckNumber, deckSuit, cardList)
    createCard(p2, deckNumber, deckSuit, cardList)
    
    print(p1.hand)
    print(p2.hand)
main()

Class Code:

class Player:
    def __init__(self, fname: str, lname: str, hand = [], money = 0):
        self.fname = fname
        self.lname = lname
        self.hand = hand
        self.money = money

    def addCard(self, newCard):
        self.hand.insert(0,newCard)

    def removeMoney(self, bet):
        self.money -= bet

    def addMoney(self, bet):
        self.money += bet

Method Code

import random
def createCard(player, deckNumber, deckSuit, cardList,):
    cardPlayer = deckNumber[random.randint(0, len(deckNumber)-1)] + " " + 
    deckSuit[random.randint(0, len(deckSuit)-1)]
    if cardPlayer not in cardList:
        cardList.append(cardPlayer)
        player.addCard(cardPlayer)

added hand = [] and then did self.hand = hand because when I did just self.hand = [] it gave me the error

Traceback (most recent call last):

File "Pythons Test Shit\Test'.py", line 17, in <module>

main()

File "Pythons Test Shit\Test'.py", line 9, in main

p1 = Player("Herp", "Derp")

TypeError: __init__() missing 1 required positional argument: 'hand'

and with hand = [] in the initializer

Player 1: ['2 Hearts', '1 Clubs']

Player 2: ['2 Hearts', '1 Clubs']

both instances are having their lists edited

edit 2:

re-read the comments and saw u/Binary101010 's comment, sorry for not trying that before doing all my edits and stuff but thank you so much, it worked

🌐
TutorialsPoint
tutorialspoint.com › how-to-create-an-empty-class-in-python
How to create an empty class in Python?
September 15, 2022 - The attributes are data members (class variables and instance variables) and methods, accessed via dot notation. We can easily create an empty class in Python using the pass statement.
Top answer
1 of 4
35

I think you should avoid both solutions. Simply because you should avoid to create uninitialized or partially initialized objects, except in one case I will outline later.

Look at two slightly modified version of your class, with a setter and a getter:

class MyClass1:
    def __init__(self, df):
          self.df = df
          self.results = None

    def set_results(self, df_results):
         self.results = df_results

    def get_results(self):
         return self.results

And

class MyClass2:
    def __init__(self, df):
          self.df = df

    def set_results(self, df_results):
         self.results = df_results

    def get_results(self):
         return self.results

The only difference between MyClass1 and MyClass2 is that the first one initializes results in the constructor while the second does it in set_results. Here comes the user of your class (usually you, but not always). Everyone knows you can't trust the user (even if it's you):

MyClass1("df").get_results()
# returns None

Or

MyClass2("df").get_results()
# Traceback (most recent call last):
# ...
# AttributeError: 'MyClass2' object has no attribute 'results'

You might think that the first case is better because it does not fail, but I do not agree. I would like the program to fail fast in this case, rather than do a long debugging session to find what happened. Hence, the first part of first answer is: do not set the uninitialized fields to None, because you loose a fail-fast hint.

But that's not the whole answer. Whichever version you choose, you have an issue: the object was not used and it shouldn't have been, because it was not fully initialized. You can add a docstring to get_results: """Always use set_results **BEFORE** this method""". Unfortunately the user doesn't read docstrings either.

You have two main reasons for uninitialized fields in your object: 1. you don't know (for now) the value of the field; 2. you want to avoid an expansive operation (computation, file access, network, ...), aka "lazy initialization". Both situations are met in real world, and collide the need of using only fully initialized objects.

Happily, there is a well documented solution to this problem: Design Patterns, and more precisely Creational patterns. In your case, the Factory pattern or the Builder pattern might be the answer. E.g.:

class MyClassBuilder:
    def __init__(self, df):
          self._df = df # df is known immediately
          # GIVE A DEFAULT VALUE TO OTHER FIELDS to avoid the possibility of a partially uninitialized object.
          # The default value should be either:
          # * a value passed as a parameter of the constructor ;
          # * a sensible value (eg. an empty list, 0, etc.)

    def results(self, df_results):
         self._results = df_results
         return self # for fluent style
         
    ... other field initializers

    def build(self):
        return MyClass(self._df, self._results, ...)

class MyClass:
    def __init__(self, df, results, ...):
          self.df = df
          self.results = results
          ...
          
    def get_results(self):
         return self.results
    
    ... other getters
         

(You can use a Factory too, but I find the Builder more flexible). Let's give a second chance to the user:

>>> b = MyClassBuilder("df").build()
Traceback (most recent call last):
...
AttributeError: 'MyClassBuilder' object has no attribute '_results'
>>> b = MyClassBuilder("df")
>>> b.results("r")
... other fields iniialization
>>> x = b.build()
>>> x
<__main__.MyClass object at ...>
>>> x.get_results()
'r'

The advantages are clear:

  1. It's easier to detect and fix a creation failure than a late use failure;
  2. You do not release in the wild a uninitialized (and thus potentially damaging) version of your object.

The presence of uninitialized fields in the Builder is not a contradiction: those fields are uninitialized by design, because the Builder's role is to initialize them. (Actually, those fields are some kind of forein fields to the Builder.) This is the case I was talking about in my introduction. They should, in my mind, be set to a default value (if it exists) or left uninitialized to raise an exception if you try to create an uncomplete object.

Second part of my answer: use a Creational pattern to ensure the object is correctly initialized.

Side note: I'm very suspicious when I see a class with getters and setters. My rule of thumb is: always try to separate them because when they meet, objects become unstable.

2 of 4
21

Following considerable research and discussions with experienced programmers please see below what I believe is the most Pythonic solution to this question. I have included the updated code first and then a narrative:

class MyClass:
    def __init__(self,df):
          self.df = df
          self._results = None

    @property
    def results(self):
        if self._results is None:
            raise Exception('df_client is None')
        return self._results

    def generate_results(self, df_results):
         #Imagine some calculations here or something
         self._results = df_results

Description of what I learnt, changed and why:

  1. All class attributes should be included in the __init__ (initialiser) method. This is to ensure readability and aid debugging.

  2. The first issue is that you cannot create private attributes in Python. Everything is public, so any partially initialised attributes (such as results being set to None) can be accessed. Convention to indicate a private attribute is to place a lead underscore at the front, so in this case I changed it to self.results to self._results.

    Keep in mind this is only convention, and self._results can still be directly accessed. However, this is the Pythonic way to handle what are pseudo-private attributes.

  3. The second issue is having a partly initialised attribute which is set to None. As this is set to None, as @jferard below explains, we now have lost a fail-fast hint and have added a layer of obfuscation for debugging the code.

    To resolve this we add a getter method. This can be seen above as the function results() which has the @property decorator above.

    This is a function that when invoked checks if self._results is None. If so it will raise an exception (fail-safe hint), otherwise it will return the object. The @property decorator changes the invocation style from a function to an attribute, so all the user has to use on an instance of MyClass is .results just like any other attribute.

    (I changed the name of the method that sets the results to generate_results() to avoid confusion and free up .results for the getter method)

  4. If you then have other methods within the class that need to use self._results, but only when properly assigned, you can use self.results, and that way the fail-safe hint is baked in as above.

I recommend also reading @jferard's answer to this question. He goes into depth about the problems and some of the solutions. The reason I added my answer is that I think for a lot of cases the above is all you need (and the Pythonic way of doing it).

🌐
TutorialsPoint
tutorialspoint.com › article › how-to-declare-an-attribute-in-python-without-a-value
How to declare an attribute in Python without a value?
class Student: def __init__(self): ... names use InitialCaps convention class CourseRegistration: pass · Use None to declare attributes without values....
Find elsewhere
🌐
Real Python
realpython.com › lessons › class-and-instance-attributes
Class and Instance Attributes (Video) – Real Python
This special keyword tells Python that this is an empty class. 02:50 Before we can add properties to our class, we have to learn about how they work in Python. First off, properties are actually called attributes in Python.
Published: March 22, 2019
🌐
Python Forum
python-forum.io › thread-10426.html
Empty attribute class dictionary after saving it in a class object dictionary
I will try to explain what problem I have: I have an attribute class dictionary called 'diccpuntscontrol' which is annoying me because for some unknown reason it's not working properly. Basically there is a problem when recovering the data of the w...
🌐
Java2s
java2s.com › example › python-book › empty-class.html
Python - Class Empty Class
Python · Class · Class Creation · The following statement makes a class with no attributes attached, an empty namespace object: class rec: pass # Empty namespace object After we make the class by running this statement, we can attach attributes to the class by assigning names to it completely outside of the original class statement: rec.name = 'Bob' # Just objects with attributes rec.age = 40 ·
🌐
Stack Overflow
stackoverflow.com › questions › 61917812 › given-an-object-in-python-can-i-somehow-leave-one-attribute-empty
Given an object in python, can I somehow leave one attribute empty? - Stack Overflow
class test: def __init__(self, _one = None, _two =None): self.one = _one self.two = _two t = test(1, 2) print(t.one) #1 print(t.two) #2 t = test(_one=1) print(t.one) #1 print(t.two) #None t = test(_two=2) print(t.one) #None print(t.two) #2 ... Sign up to request clarification or add additional context in comments. ... Yes, you could pass None to the constructor. ... Just be careful to check for None before doing arithmetic with the attributes, because that will raise a TypeError.
🌐
Toptal
toptal.com › python › python-class-attributes-an-overly-thorough-guide
Python Class Attributes: An Overly Thorough Guide | Toptal®
January 16, 2026 - Stuck to instance attributes entirely, as demonstrated in the introduction. Avoided using the empty list (a mutable value) as our “default.” ... Of course, we’d have to handle the None case appropriately, but that’s a small price to pay. Class attributes are tricky, but let’s look at a few cases where they would come in handy:
🌐
Finxter
blog.finxter.com › home › learn python blog › 5 best ways to create an empty class in python
5 Best Ways to Create an Empty Class in Python - Be on the Right Side of Change
February 28, 2024 - A more dynamic way to create an empty class in Python is through the built-in type function. This function can be used as a dynamic class factory when passed with the name of the new class, a tuple containing its base classes (empty in this case), and a dictionary that serves as the namespace for class attributes (also empty).
🌐
Quora
quora.com › Why-would-you-want-to-have-an-empty-class-in-Python
Why would you want to have an empty class in Python? - Quora
Answer (1 of 2): The most common case is for defining your own custom exceptions. Those are normally empty classes inheriting from the most appropriate existing exception in the hierarchy. Another use is to create a sort of flexible and lightweight “struct” … a container or data hub to ...
Top answer
1 of 3
1

You can define a dict as a class variable as you already did, but add the UID as a key to the dict in the __init__ method instead of a separate add_user method so that you can always validate the UID when an object is instantiated, no matter how:

class Account():
    users = {}
    def __init__(self, name, balance=0.0, uid=None):
        if uid in self.users:
            raise ValueError("UID '%s' already belongs to %s." % (uid, self.users[uid].name))
        if len(uid) != 5 or not uid.isdigit():
            raise ValueError("UID must be a 5-digit number.")
        self.name = name
        self.balance = balance
        self.uid = uid
        self.users[uid] = self
2 of 3
0

First noticed that you cant to a "return print(...", remove print.

You can do something like this

class Account():
'''
A class to perform some basic banking functions
'''
UserList = {}   #Empty dictionary to store (UID: name) for each new instance
def __init__(self, name, balance=0.0, uid=None):
    self.name = name    #The name of the account holder
    self.balance = balance #The initial balance
    self.uid = uid  #User ID number chosen by account holder
    self.add_user(uid, name)

@classmethod
def new_account(cls):
    '''
    New user can specify details of account through this class method via input()
    '''
    return cls(
        input('Name: '),
        int(input('Balance: ')),
        int(input('UID: ')),
    )

def withdraw(self, amount):
    if amount > self.balance:
        raise RuntimeError('Amount greater than available balance.')
    else:
        self.balance -= amount
        return "After a withdrawl of {}, {}'s current balance is {}".format(amount, self.name, self.balance) #printing balance after withdrawl

def deposit(self, amount):
    self.balance += amount
    return "After a deposit of {}, {}'s curent balance is {}".format(amount, self.name, self.balance) # printing balance after deposit

def add_user(self, uid, name):
    self.UserList[int(uid)] = name

a = Account("new user", 100, 1)

a.add_user(2, "new user") a.add_user(3, "new user")

print(a.UserList)

this will output {1: 'new user', 2: 'new user', 3: 'new user'}