Creating dictionary-like classes may be a requirement in your Python career. Specifically, you may be interested in making custom dictionaries with modified behavior, new functionalities, or both. In Python, you can do this by inheriting from an , by subclassing the built-in 7 class directly, or by inheriting from . Show
In this tutorial, you’ll learn how to:
Additionally, you’ll code a few examples that’ll help you understand the pros and cons of using 7 vs 8 to create your custom dictionary classes.To get the most out of this tutorial, you should be familiar with Python’s built-in 7 class and its standard functionality and features. You’ll also need to know the basics of object-oriented programming and understand how inheritance works in Python.Join Now: Click here to join the Real Python Newsletter and you'll never miss another Python tutorial, course update, or post. Creating Dictionary-Like Classes in PythonThe built-in 7 class provides a valuable and versatile collection data type, the Python dictionary. Dictionaries are everywhere, including in your code and the code of Python itself.Sometimes, the standard functionality of Python dictionaries isn’t enough for certain use cases. In these situations, you’ll probably have to create a custom dictionary-like class. In other words, you need a class that behaves like a regular dictionary but with modified or new functionality. You’ll typically find at least two reasons for creating custom dictionary-like classes:
Note that you could also face situations in which you need to both extend and modify the dictionary’s standard functionality. Depending on your specific needs and skill level, you can choose from a few strategies for creating custom dictionaries. You can:
There are a few key considerations when you’re selecting the appropriate strategy to implement. Keep reading for more details. Remove adsBuilding a Dictionary-Like Class From an Abstract Base ClassThis strategy for creating dictionary-like classes requires that you inherit from an abstract base class (ABC), like . This class provides concrete generic implementations of all the dictionary methods except for , , , , and , which you’ll have to implement by yourself. Additionally, suppose you need to customize the functionality of any other standard dictionary method. In that case, you’ll have to override the method at hand and provide a suitable implementation that fulfills your needs. This process implies a fair amount of work. It’s also error-prone and requires advanced knowledge of Python and its data model. It can also imply performance issues because you’ll be writing the class in pure Python. The main advantage of this strategy is that the parent ABC will alert you if you miss any method in your custom implementation. For these reasons, you should embrace this strategy only if you need a dictionary-like class that’s fundamentally different from the built-in dictionary. In this tutorial, you’ll focus on creating dictionary-like classes by inheriting from the built-in 7 class and the 8 class, which seem to be the quickest and most practical strategies.Inheriting From the Python Built-in >>> numbers = UpperCaseDict({"one": 1, "two": 2, "three": 3}) >>> numbers {'one': 1, 'two': 2, 'three': 3} 7 ClassFor a long time, it was impossible to subclass Python types implemented in C. Python 2.2 fixed this issue. Now you can , including 7. This change brings several technical advantages to the subclasses because now they:
The first item in this list may be a requirement for C code that expects a Python built-in class. The second item allows you to add new functionality on top of the standard dictionary behavior. Finally, the third item will enable you to restrict the attributes of a subclass to only those attributes predefined in 1.Even though subclassing built-in types has several advantages, it also has some drawbacks. In the specific case of dictionaries, you’ll find a few annoying pitfalls. For example, say that you want to create a dictionary-like class that automatically stores all its keys as strings where all the letters, if present, are uppercase. To do this, you can create a subclass of 7 that overrides the 3 method:>>>
Cool! Your custom dictionary seems to work well. However, there are some hidden issues in this class. If you try to create an instance of 6 using some initialization data, then you’ll get a surprising and buggy behavior:>>>
What just happened? Why doesn’t your dictionary convert the keys into uppercase letters when you call the class’s constructor? It looks like the class’s initializer, 7, doesn’t call 3 implicitly to create the dictionary. So, the uppercase conversion never runs.Unfortunately, this issue affects other dictionary methods, like and , for example: >>>
Again, your uppercase functionality isn’t working well in these examples. To solve this issue, you must provide custom implementations of all the affected methods. For example, to fix the initialization issue, you can write an 7 method that looks something like this:
Here, 7 converts the keys into uppercase letters and then initializes the current instance with the resulting data.With this update, the initialization process of your custom dictionary should work correctly. Go ahead and give it a try by running the following code: >>>
Providing your own 7 method fixed the initialization issue. However, other methods like 9 continue to work incorrectly, as you can conclude from the 5 key’s not being uppercase.Why do 7 subclasses behave this way? Built-in types were designed and implemented with the open–closed principle in mind. Therefore, they’re open to extension but closed to modification. Allowing modifications to the core features of these classes can potentially break their . So, Python core developers decided to protect them from modifications.That’s why subclassing the built-in 7 class can be a little bit tricky, labor-intensive, and error-prone. Fortunately, you still have alternatives. The 8 class from the 2 module is one of them.Remove adsSubclassing >>> numbers = UpperCaseDict({"one": 1, "two": 2, "three": 3}) >>> numbers {'one': 1, 'two': 2, 'three': 3} 8 From >>> numbers = UpperCaseDict() >>> numbers["one"] = 1 >>> numbers["two"] = 2 >>> numbers["three"] = 3 >>> numbers {'ONE': 1, 'TWO': 2, 'THREE': 3} >>> numbers.update({"four": 4}) >>> numbers {'ONE': 1, 'TWO': 2, 'THREE': 3, 'four': 4} >>> numbers.setdefault("five", 5) 5 >>> numbers {'ONE': 1, 'TWO': 2, 'THREE': 3, 'four': 4, 'five': 5} 2Starting with Python 1.6, the language has provided 8 as part of the standard library. This class initially lived in a module named after the class itself. In Python 3, 8 was moved to the 2 module, which is a more intuitive place for it, based on the class’s primary purpose. 8 was created back when it was impossible to inherit from Python’s 7 directly. Even though the need for this class has been partially supplanted by the possibility of subclassing the built-in 7 class directly, 8 is still available in the standard library, both for convenience and for backward compatibility. 8 is a convenient wrapper around a regular 7 object. This class provides the same behavior as the built-in 7 data type with the additional feature of giving you access to the underlying dictionary through the instance attribute. This feature can facilitate the creation of custom dictionary-like classes, as you’ll learn later in this tutorial. 8 was specially designed for subclassing purposes rather than for direct instantiation, which means that the class’s main purpose is to allow you to create dictionary-like classes through inheritance.There are also other hidden differences. To uncover them, go back to your original implementation of 6 and update it like in the code below:>>>
This time, instead of inheriting from 7, you’re inhering from 8, which you imported from the 2 module. How will this change affect the behavior of your 6 class? Check out the following examples:>>>
Now 6 works correctly all the time. You don’t need to provide custom implementations of 7, 9, or 0. The class just works! This is because in 8, all the methods that update existing keys or add new ones consistently rely on your 3 version.As you learned before, the most notable difference between 8 and 7 is the 2 attribute, which holds the wrapped dictionary. Using 2 directly can make your code more straightforward because you don’t need to call 9 all the time to provide the desired functionality. You can just access 2 and work with it as you would with any regular dictionary.Coding Dictionary-Like Classes: Practical ExamplesYou already know that subclasses of 7 don’t call 3 from methods like 9 and 7. This fact makes subclasses of 7 behave differently from a typical Python class with a 3 method.To work around this issue, you can inherit from 8, which does call 3 from all the operations that set or update values in the underlying dictionary. Because of this feature, 8 can make your code safer and more compact.Admittedly, when you think of creating a dictionary-like class, inheriting from 7 is more natural than inhering from 8. This is because all Python developers know about 7, but not all Python developers are aware of the existence of 8.Inheriting from 7 often implies certain issues that you can probably fix by using 8 instead. However, these issues aren’t always relevant. Their relevance very much depends on how you want to customize the dictionary’s functionality.The bottom line is that 8 isn’t the right solution all the time. In general, if you want to extend the standard dictionary without affecting its core structure, then it’s totally okay to inherit from 7. On the other hand, if you want to change the core dictionary behavior by overriding its special methods, then 8 is your best alternative.In any case, remember that 7 is written in C and is highly optimized for performance. In the meantime, 8 is written in pure Python, which can represent a significant limitation in terms of performance.You should consider several factors when deciding whether to inherit from 7 or 8. These factors include, but aren’t limited to, the following:
In the following section, you’ll experience the first three factors in this list by coding a few practical examples. You’ll learn about performance implications a bit later, in the section on . Remove adsA Dictionary That Accepts British and American Spelling of KeysAs the first example, say you need a dictionary that stores keys in American English and allows key lookup in either American or British English. To code this dictionary, you’ll need to modify at least two , 3 and 2.The 3 method will allow you to always store keys in American English. The 2 method will make it possible to retrieve the value associated with a given key, whether it’s spelled in American or British English.Because you need to modify the core behavior of the 7 class, using 8 would be a better option to code this class. With 8, you won’t have to provide custom implementations of 7, 9, and so on.When you subclass 8, you have two main ways to code your class. You can rely on the 2 attribute, which may facilitate the coding, or you can rely on 9 and special methods.Here’s the code that relies on 2:
In this example, you first define a constant, 26, containing the British words as keys and the matching American words as values.Then you define 27, inheriting from 8. The 2 method looks for the current key. If the key exists, then the method returns it. If the key doesn’t exist, then the method checks if the key was spelled in British English. If that’s the case, then the key is translated to American English and retrieved from the underlying dictionary.The 3 method tries to find the input key in the 26 dictionary. If the input key exists in 26, then it’s translated to American English. Finally, the method assigns the input 33 to the target 34.Here’s how your 27 class works in practice:>>>
By subclassing 8, you’re saving yourself from writing a lot of code. For example, you don’t have to provide methods like 37, 9, or 0, because their default implementations will automatically rely on your 2 and 3 methods.If you have less code to write, then you’ll have less work to do. More importantly, you’ll be safer because less code often implies a lower risk of bugs and errors. Note: If you need that the keyword works with both spellings, then you’ll have to implement a custom 4 method in your 27 dictionary. Similarly, if you want to work with both spellings, then you’ll have to override the 45 method.The main drawback of this implementation is that if you someday decide to update 27 and make it inherit from 7, then you’ll have to rewrite most of the code to suppress the use of 2.The example below shows how to provide the same functionality as before using 9 and some special methods. This time, your custom dictionary is fully compatible with 7, so you can change the parent class anytime you like:
This implementation looks slightly different from the original one but works the same. It could also be harder to code because you’re not using 2 anymore. Instead, you’re using 9, 2, and 3. This code requires certain knowledge of Python’s data model, which is a complex and advanced topic.The main advantage of this new implementation is that your class is now compatible with 7, so you can change the super class at any time if you ever need to do so.Note: Remember that if you inherit directly from 7, then you need to reimplement 7 and other methods so that they also translate keys to American spelling when the keys are added to the dictionary.It’s often more convenient to extend the standard dictionary functionality by subclassing 8 than by subclassing 7. The main reason is that the built-in 7 has some implementation shortcuts and optimizations that end up forcing you to override methods that you can just inherit if you use 8 as the parent class.Remove adsA Dictionary That Accesses Keys Through ValuesAnother common requirement for a custom dictionary is to provide additional functionality apart from the standard behavior. For example, say that you want to create a dictionary-like class that provides methods to retrieve the key that maps to a given target value. You need a method that retrieves the first key that maps to the target value. You also want a method that returns an iterator over those keys that map to equal values. Here’s a possible implementation of this custom dictionary: 0This time, instead of inheriting from 8, you’re inheriting from 7. Why? In this example, you’re adding functionality that doesn’t alter the dictionary’s core features. Therefore, inheriting from 7 is more appropriate. It’s also more efficient in terms of performance, as you’ll see later in this tutorial.The 65 method iterates over the key-value pairs in the underlying dictionary. The conditional statement checks for values that match the target value. The 66 code block returns the key of the first matching value. If the target key is missing, then the method raises a 67.As a generator method that yields keys on demand, 68 will yield only those keys whose value matches the 33 provided as an argument in the method call.Here’s how this dictionary works in practice: >>> 1Cool! Your 70 dictionary works as expected. It inherits the core dictionary’s features from Python’s 7 and implements new functionality on top of that.In general, you should use 8 to create a dictionary-like class that acts like the built-in 7 class but customizes some of its core functionality, mostly special methods like 3 and 2.On the other hand, if you just need a dictionary-like class with extended functionality that doesn’t affect or modify the core 7 behavior, then you’re better off to inherit directly from 7 in Python. This practice will be quicker, more natural, and more efficient.A Dictionary With Additional FunctionalitiesAs a final example of how to implement a custom dictionary with additional features, say that you want to create a dictionary that provides the following methods: MethodDescription 78Takes a callable 79 as an argument and applies it to all the values in the underlying dictionary 80Removes a given 34 from the underlying dictionary 82Returns 83 or 84 depending on whether the dictionary is empty or notTo implement these three methods, you don’t need to modify the core behavior of the built-in 7 class. So, subclassing 7 rather than 8 seems to be the way to go.Here’s the code that implements the required methods on top of 7: 2In this example, 89 takes a callable as an argument and applies it to every value in the underlying dictionary. The transformed value is then reassigned to the original key. The 90 method uses the statement to remove the target key from the dictionary. Finally, 82 uses the built-in 93 function to find out if the dictionary is empty or not.Here’s how 94 works:>>> 3In these examples, you first create an instance of 94 using a regular dictionary as an argument. Then you call 89 on the extended dictionary. This method takes a 97 function as an argument and applies it to every value in the dictionary, transforming the target value into its square.Then, 90 takes an existing key as an argument and removes the corresponding key-value pair from the dictionary. Finally, 82 returns 84 because 01 isn’t empty. It would have returned 83 if the underlying dictionary was empty.Remove adsConsidering PerformanceInheriting from 8 may imply a performance cost because this class is in pure Python. On the other hand, the built-in 7 class is written in C and highly optimized for performance. So, if you need to use a custom dictionary in performance-critical code, then make sure to time your code to find potential performance issues.To check if performance issues can arise when you inherit from 8 instead of 7, get back to your 94 class and copy its code into two different classes, one inheriting from 7 and the other inheriting from 8.Your classes should look something like this: 4The only difference between these two classes is that 10 subclasses 7, and 12 subclasses 8.To check their performance, you can start by timing core dictionary operations, such as class instantiation. Run the following code in your Python interactive session: >>> 5In this code snippet, you use the module along with the 15 function to measure the execution time of a piece of code. In this example, the target code consists of instantiating 10 and 12.Once you’ve run this time-measuring code, then you compare both initialization times. In this specific example, the initialization of the class based on 8 is slower than the class derived from 7. This result is an indicator of a serious performance difference.Measuring the execution time of new functionalities may also be interesting. For example, you can check the execution time of 89. To do this check, go ahead and run the following code:>>> 6The performance difference between the class based on 8 and the class based on 7 isn’t that big this time, but it still exists.Often, when you create a custom dictionary by subclassing 7, you can expect standard dictionary operations to be more efficient in this class than in a class based on 8. On the other hand, new functionality may have similar execution time in both classes. How would you know which is the most efficient way to go? Well, you have to time-measure your code.It’s worth noting if you’re aiming to modify the core dictionary functionality, then 8 is probably the way to go because, in this case, you’ll be mostly rewriting the 7 class in pure Python.ConclusionNow you know how to create custom dictionary-like classes with modified behavior and new functionalities. You’ve learned to do this by subclassing the built-in 7 class directly and by inheriting from the 8 class available in the 2 module.In this tutorial, you learned how to:
You’ve also written some practical examples that helped you understand the pros and cons of using 8 vs 7 when creating your custom dictionary classes.You’re now ready to create your custom dictionaries and to leverage the full power of this useful data type in Python in response to your coding needs. Join Now: Click here to join the Real Python Newsletter and you'll never miss another Python tutorial, course update, or post. Mark as Completed 🐍 Python Tricks 💌 Get a short & sweet Python Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team. Send Me Python Tricks » About Leodanis Pozo Ramos Leodanis is an industrial engineer who loves Python and software development. He's a self-taught Python developer with 6+ years of experience. He's an avid technical writer with a growing number of articles published on Real Python and other sites. » More about LeodanisEach tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are: Aldren Bartosz Geir Arne Kate Master Real-World Python Skills With Unlimited Access to Real Python Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas: Level Up Your Python Skills » Master Real-World Python Skills Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas: Level Up Your Python Skills » What Do You Think? Rate this article: Tweet Share Share EmailWhat’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know. Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students. and get answers to common questions in our support portal. Apa itu dictionary di Python?4. Dictionary
Tipe data dictionary akan digunakan untuk memetakan sebuah nilai pada data ke dalam nilai lainnya. Tipe data dictionary dapat dideklarasikan dengan diawali oleh tanda kurung buka kurawal ( { ), memisahkan setiap elemen di dalamnya dengan tanda koma ( , ) dan ditutup dengan tanda kurung tutup ( } ).
Bagaimana cara mengakses tuple?Untuk mengakses nilai dalam Tuple, maka kamu bisa menggunakan tanda kurung siku untuk mengiris beserta indeks agar mendapatkan nilai yang tersedia pada indeks tersebut.
Apa fungsi dictionary?Dictionary merupakan tipe data pada Python yang berfungsi untuk menyimpan kumpulan data atau nilai, yang setiap urutanya berisi key dan value.
Jelaskan apa yang dimaksud dengan key value pada Dictionary?Data dictionary terdiri dari pasangan "value-key". "key" mengidentifikasi item, dan "value" menyimpan nilai item yang akan dipisahkan oleh tanda titik dua. Item dipisahkan dengan koma dan diapit tanda kurung kurawal. "key" tidak bisa diubah sedangkan "value" dapat berupa tipe data jenis apapun.
|