# Python Properties vs Methods: The Contract You Didn’t Know You Were Making

## Метаданные

- **Канал:** ArjanCodes
- **YouTube:** https://www.youtube.com/watch?v=wHLZ_uTrCYA
- **Просмотры:** 54,748

## Описание

🧱 Build software that lasts. Join the Software Design Mastery waiting list → https://arjan.codes/mastery.

In this video, I break down when something in Python should be a property and when it should be a method, and why that choice matters more than it looks. Using a simple example, I show how properties and methods communicate different promises about cost, safety, and behaviour.

I’ll also look at why async properties are usually a design smell, and how to keep async work explicit without breaking clean object design.

🔥 GitHub Repository: https://git.arjan.codes/2026/props.

🎓 ArjanCodes Courses: https://www.arjancodes.com/courses.

💬 Join my Discord server: https://discord.arjan.codes.

⌨️ Keyboard I’m using: https://amzn.to/49YM97v.

🔖 Chapters:
0:00 Intro
1:17 Setup: One Example for the Whole Video (Synchronous Version)
2:29 Properties vs Methods
3:48 Derived State: A Perfect Use Case for Properties
6:14 Under the Hood: Why @property Works
6:59 Read-Only vs Writable Properties
8:58 When Setters Should Not Do I/O
10:29 Abstractions: Properties Are Part of the Contract
13:13 Question: Can a Property Be Async?
16:09 Final Thoughts

#arjancodes #softwaredesign #python

## Содержание

### [0:00](https://www.youtube.com/watch?v=wHLZ_uTrCYA) Intro

Look at these two user account classes. Aren't they beautiful? No, they're not really beautiful. But as you can see, the main difference is that here, this one has a method and this user account class has a property for roughly the same thing. And one of these is a better design choice than the other. So today I'll talk about when to use a method and property in general and give you a few easy guidelines to make that decision yourself next time. And maybe you have ever wondered whether a property can be asynchronous. If you're a normal person, you probably haven't wondered about it. But if you're like weird like me, then maybe you have. Watch the video until the end to find out because I'm going to talk about that as well. Now, if you care about software design, not just [clears throat] making your code work, but designing code that stays clear, predictable, easy to change, I'm building something new. It's a program called Software Design Mastery. It's way more than just another online course. It covers literally everything I know about software design from years of teaching, consulting, developing, reviewing real production code. If you want to be the first to know when it opens, don't want to miss early access. Join the wait list. The link is also in the video description. Now, like I said, we have

### [1:17](https://www.youtube.com/watch?v=wHLZ_uTrCYA&t=77s) Setup: One Example for the Whole Video (Synchronous Version)

these two user accounts here. There is a username and an email address, and that's basically it. So why would you use a method here versus a property? Actually, if you go back to older languages like Java, they didn't have properties. So you only had getters and setters. So you had to do something like this always in Java. Now the nice thing about properties is that they behave a bit like attributes, right? In this case, for example, let's see how you would use that. So I have my user account data class. So if I create a user account then I can print the username by calling this getter method. Pretty straightforward. So uh when I run this then as you can see this is what we get as a result. Simply prints the username. But let's say I'm using user account 2 and now of course I don't use this getter but I simply access the property directly and then this is what we get and we have exactly the same output. But of course this way of accessing the username is way easier than using this method. It's shorter. You don't have to write the parenthesis. So it's really easy way to access data. And that also informs what

### [2:29](https://www.youtube.com/watch?v=wHLZ_uTrCYA&t=149s) Properties vs Methods

a property actually communicate. It communicates that something is stelike, that it's cheap to access, that it's safe to read repeatedly. Now method typically communicates something else. It tells you that probably some work is happening. We don't know exactly what will happen in a method. A method can be something very simple, but it can also be extremely complex. It might communicate with the back end or a database or something like that. It communicates that well something might fail. You might get an exception or other things might happen. A method might be slow. So if you keep these two things in mind, it also shows you when to typically use a property versus when to use a method. So if you know that something takes a lot of work, it's complicated and you want that to be clear to the user, use a method. If you want to stress that something is easy to access, use a property. So in this particular case, I would always use the property instead of the method because this is simply something that we just want to return a state. There's no complicated things going on. Now, another thing you could do, let me just remove this class because we're going to keep the property version. Going to call this user account now. There we go. We're all set. Now, another thing that

### [3:48](https://www.youtube.com/watch?v=wHLZ_uTrCYA&t=228s) Derived State: A Perfect Use Case for Properties

properties are really helpful for, and that's kind of similar to how we're using properties here, is the derived state. So, basically, if you need to compute something simple based on existing state, let's say we want to keep track of the status of an account. So an account can be active, it can be suspended, it can be cancelled. There's maybe a few other options we can think of. So let's say from enum we're going to import a string enum to represent account status. So I'm going to have a class account status. This is going to be a string enum and it can be let's say active. By the way, you can also use auto for this. I'm just doing it like this because it's easier. We can have suspended and let's say we have closed. So these are the statuses and then our user account can have a status of type account status. Let's say Mason is active like so. And I'm just going to print the account. So we have all the information here. So as you can see it now has the account status. So you might want to have a property to indicate whether or not the user is active. And that property would then simply return a boolean value. So the way you would do that is we define a property is active that returns a boolean. And this is going to return self. us status equals account status dot active like so. And actually I think we should do the comparison like so. And now we can print user is active is active. Let's see. Yes, the user is active because that's also the account status. If I change this to let's say suspended then this is going to return false. So this way of defining properties works really well because well it's deterministic. It has no side effects. It reads just some existing state and that also means as you've seen if you change the status then status will always reflect that correctly. You don't have to keep track of redundant data.

### [6:14](https://www.youtube.com/watch?v=wHLZ_uTrCYA&t=374s) Under the Hood: Why @property Works

Now you might wonder what is actually happening with this decorator. How does that actually work? Well, property actually uses the descriptor protocol. What the descriptor protocol does is that it defines basically what uh reading a value and assigning to a value actually does. So if you implement that protocol, you can specify what should happen if you assign a value to that thing or if you read from that thing. So that's what a property actually does and specifically the property decorator specifies what should happen if you read a thing. not writing, it's just reading. So that also means that is active currently is read only and in essence it's still a method but it's simply because of the decorator it's disguised as an attribute. So like I

### [6:59](https://www.youtube.com/watch?v=wHLZ_uTrCYA&t=419s) Read-Only vs Writable Properties

said is active is currently a read only property. So if I do let's say account do is active is false and then I try to print that here you can see that we now get an error that this property does not have a setter. So we're not allowed to do that because we only defined it like so. If you want to specify it as a setter, that's also possible. But then you have to do is active dot setter. And then what we're going to do is if value self dot status equals account status dot active else and let's say that if we set it to false then the account status is going to be closed. Of course we can do a direct mapping here easily. We have to make some assumptions because there is multiple states here like so. And now I can actually write to this. So then you see that the account status is now closed and the user is no longer active. So properties can be read only. In that case you only define uh this part or properties can be write as well. And then you can specify this part. You might wonder is it possible to have a write property? Uh no unfortunately not because if I put this into comments like so now we have only the right part. and it no longer recognizes this decorator. So if I try to run this code, you see that we get a name error. So that doesn't work. A property can't be write only. It needs to be read only or read and write. Let me uncomment this again. Another thing you might wonder about is if you do these types of assignments like I'm doing right here, should assigning potentially trigger side effects like updating something in a database or sending some data over a network or whatever? For example, what

### [8:58](https://www.youtube.com/watch?v=wHLZ_uTrCYA&t=538s) When Setters Should Not Do I/O

you could imagine you would do is that there is a save method like so that saves the object to the database. So for now, let's just print that like so. And of course, this needs to get self like so. And then I can set the is active status. save it to a database like so. That's self. Okay, there we go. So, when you run this, you see that when I write to the property, it saves to the database. So, you might think, oh, that's actually maybe a nice thing to do. That way, if we update the object, it's going to be stored automatically. However, the problem is that you're now kind of breaking the promise of properties. If you do some IO, this might fail. It might block for some reason, and it kind of breaks the property promise. Now better design is to keep the property local and then introduce an explicit method that actually saves it to the database. So you wouldn't call it right here. Uh you would actually do it explicitly in your main function. So we would do account. save to actually store things store the changes in the database. That also allows you to do multiple updates and then commit them all at once. So it's way cleaner. And then if I run this then of course we get uh this result. Now doing some validation in setters is fine but if you're doing persistence IO I think it should be explicit in your designs. Another thing I want to touch

### [10:29](https://www.youtube.com/watch?v=wHLZ_uTrCYA&t=629s) Abstractions: Properties Are Part of the Contract

on is abstraction. So let's say from typing I import protocol and let's say somewhere we're dealing with user accounts but we don't want actual user accounts. We simply want to define the interface. So what you could do is let's say we have a class user account view which is a protocol and of course user account it has this kind of internal representations right of the username the email and the status and we only have a few properties here. So how do you specify that in a protocol? So property is actually really easy. So I can define a property here just like I do in a regular class of username that returns a string just like we would define a normal method. But now we know when we have this protocol that it has a property called username. So that works as expected and username here is read only. But then how do you specify is active? because if is active is also a property but you can also write it. So should we uh copy this over and also do the setter to the protocol. So let's say you would then define something like this and then of course here you would do it like this. Right? So this is possible to do it in this way. Uh so now we specified that is active is readable and it is writable. And then how you would use in a function is let's say do something. This gets a user account view. And then let's say we're going to print uh user dot is active. It's property and user is active is false. And then what you can do here is in my main function I'm just going to call do something and pass the account like so. Let's run this. As you can see that works. The user is no longer active. So it does exactly the same thing. So this is a way to specify a read and write property. However, there's an easier way to do this. So instead of doing all this work, you can also simply write is active is a boolean value like so similar to how you would do it in a data class. And then when I run this, we get of course exactly the same result. Now that's not surprising. Python ignores type annotations at runtime. But this is an easy way to define a read and write property. And if it's read only, then this is how you can do it. Now finally

### [13:13](https://www.youtube.com/watch?v=wHLZ_uTrCYA&t=793s) Question: Can a Property Be Async?

the question I posed in the beginning of the video, can a property be async? And for that I created another example. So here I have a bad user account which has a user ID and a couple of other things as well. And then I have a property username which is async and it's actually calling fetch username on a repository which is asynchronous. So this likely goes to a database to retrieve the information that we need. Or here is another property that uh gets a status also from the repository and then it checks whether the status is active. And then the way you would use this is that you would do an await on the property like so or here an await on is active. And if I run this, you see that this actually works. It actually does this. It fetches these things asynchronously. So yes, you can have an async property. But again, you're kind of breaking the property promise here. property is supposed to be simple and deterministic and shouldn't fail easily. So that means that I would never create async properties like this. I think it's just bad design to do that because well you're breaking the simplicity of what a property actually is. An alternative because maybe you still want to fetch this data from a database, right? And you want to deal with it. So how would you set that up? So instead of this bad user account, I created another version of this code that actually also has asynchronous operations in it, but it's way simpler, way neater. So in this case, what I did is I created a data class user account. You can also define properties in here if you wanted to, whatever you like. And then I have a class method that is asynchronous that actually loads the user. So here I'm doing all the fetching from the uh repository. And because I'm fetching this all at once, I can do asynco. gather which basically sends these off at the same time. So it's also even faster. And then I simply create the instance from that data. And then I can have a property which is non async that then simply checks the status. And we do it in the way that we did this in the example I showed you before. And similarly we can also have a save method that is asynchronous. So loading and saving in this case is asynchronous. use the repository and directly accessing the account like we're doing here is actually synchronous and that keeps things simple. So then you can print the status, update the status, change the email address or print it all synchronously and then when you're done modifying the object, you save it again asynchronously. This is in my opinion the better design and you can also see that working right here. And by the way, if you're enjoying this kind of design focused Python content, make sure to like the video, subscribe to my channel. It really helps me out a lot. In short, if you're

### [16:09](https://www.youtube.com/watch?v=wHLZ_uTrCYA&t=969s) Final Thoughts

dealing with asynchronous code that implies waiting, scheduling, potential failure, and if you make a property async, you're hiding all of that behind attribute access. And that means you get weird performance surprises, confusing behavior. uh if something needs a weight in general it's a pretty strong signal that you should use a method for that. So how do I use properties? They're purely for state derived state. They should be cheap. They should be deterministic. Methods are for actual work IO async stuff. But do you agree with me or do you use properties and methods differently? Let me know in the comments. If you enjoyed this video, you probably also like this one where I explore some lesserk known features of data classes and how they support clean object design. Thanks for watching and see you next

---
*Источник: https://ekstraktznaniy.ru/video/20755*