# Python Property

## Overview
```python
class dog:
	def __init__(self):
		self._name = "Bello"

	@property
	def name(self) -> str:
		print("Setter method called")
		return self._name

	@name.setter
	def name(self, name):
		if isinstance(name, str):
			self._name = name
			print("Getter method called")
		else:
			return
```
```python
snowy = dog()
snowy.name = "Snowy"
snowy.name
```
```python
Setter method called
Getter method called
'Snowy'
```

## Benefits
- **@property**: for the caller name becomes an attribute (not a method any more)
    - no more use of parentheses when calling a getter: `a = snowy.name` instead of `a = snowy.name()`
    - the hidden getter still can return a deep copy or converted attribute (bytearray instead of byte, list instead of set, ...)
- **@name.setter**: for the caller name can set by `=`
    - no more use of parentheses when calling a setter: `snowy.name = "Snowy"` instead of `snow.name("Snowy")`
    - the hidden setter still can use type checks or throw errors or do conversions
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9