attribute
https://medium.com/swlh/attributes-in-python-6-concepts-to-know-1db6562057b1
OOP中对象带有的参数,叫做 field attribute property
python中把这种参数叫做 attribute
In the modern programming world, object-oriented programming (OOP) languages have played an evolutionary role in changing the design and implementation patterns in software development. As a critical member of the OOP family, Python has gradually gained popularity in the last 10 years or so. Like other OOP languages, Python operates its data around a vast diversity of objects, including modules, classes, and functions.
If you have any coding experience in any OOP language, you should probably know that all objects have their internal characteristics data called fields, attributes, or properties. In Python, these object-bound characteristics data are commonly known as attributes. In this article, I would like to talk about them, specifically in the context of a custom class.
class attribute
类中定义的属性。
定义形式在 class的下一级。
这种属性为此class拥有, 为所有此class的instance共享。
>>> class Dog: ... genus = "Canis" ... family = "Canidae" ... >>> Dog.genus 'Canis' >>> Dog.family 'Canidae' >>> dir(Dog) ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'family', 'genus']
如上python使用 dir 函数来查看类的 属性列表。
As shown above, we declared a class called
Dog
. Because all dogs belong to the Canis genus and Canidae family (Wikipedia), we created two class attributes namedgenus
andfamily
to store these two pieces of information. As you can see, we could directly use the class to access these attributes. We can use the introspection functiondir
to show the list of theDog
’s attributes, which include family and genus.
Instance Attributes
实例的属性,定义在 __init__ 函数中, 依附于self对象, self就是实例对象。
这些属性为本实例独享, 每生成一个实例则这些属性都会被创建, 所以一些常量,和所有实例共享内容,都应该定义为 class属性。
With custom classes, we can also set attributes to instance objects. These attributes are known as instance attributes, which means that they are instance-specific data. Let’s continue with the
Dog
class.
In the above code, we defined the
__init__
function, which will serve as the construction method to create a newDog
instance. The first argumentself
refers to the instance that we’re creating. During the instantiation (i.e., creating the new instance), we will assign the breed and name information to the new instance object, and these attributes will become part of the instance’s characteristics, as shown below.
>>> class Dog: ... genus = "Canis" ... family = "Canidae" ... ... def __init__(self, breed, name): ... self.breed = breed ... self.name = name ...
>>> dog = Dog("Rottweiler", "Ada") >>> dog.name 'Ada' >>> dog.breed 'Rottweiler'
Functions As Attributes
属性不仅仅是一般变量, 还可以是函数。
In Python, everything is an object, and previously, I already mentioned that classes are objects. Moreover, functions are Python objects. Within a class, we can define functions, which are commonly referred to as methods. Depending on how the functions are used, we can further categorize them as class methods, static methods, and instance methods. It’s not essential here to understand these differences (see my previous articles: this and that, if you want to learn them).
Although some OOP languages treat attributes (or properties) and functions as different entities, Python considers these methods (functions) as attributes of the class — not very different as the class attributes that we defined earlier. Let’s just update the
Dog
class with all three kinds of methods as mentioned above: class, static, and instance methods, as shown below.
>>> class Dog: ... genus = "Canis" ... family = "Canidae" ... ... def __init__(self, breed, name): ... self.breed = breed ... self.name = name ... ... @classmethod ... def from_tag(cls, tag_info): ... breed = tag_info["breed"] ... name = tag_info["name"] ... return cls(breed, name) ... ... @staticmethod ... def can_bark(): ... print("Yes. All dogs can bark.") ... ... def bark(self): ... print("The dog is barking.")
>>> dir(Dog) ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bark', 'can_bark', 'family', 'from_tag', 'genus']
Private Attributes
私有属性归于本实例内部使用, 不能被实例调用者使用。
私有属性,命名以双下划线开头, 尾部不带有下划线。不与 魔法函数冲突。
If you have any experience in OOP, you shouldn’t be unfamiliar with the existence of access modifiers, such as public, private, and protected. These modifiers restrict the scope of where the modified attributes and functions can be accessed. However, you rarely hear such a discussion in Python. Practically, all Python attributes are public, if we borrow the terminology in the OOP world. As shown above, the class and instance attributes are all freely accessible where these classes and instances are accessible. Thus, strictly speaking, there are no real private or protected attributes (to be discussed next) in Python. We’re just using these terms analogously, such that it’ll be easier for programmers coming from other OOP background to appreciate related coding conventions (yes, just a convention, not reinforced as real access control).
Let’s first talk about how we can define a “private” attribute in Python. The convention is to name those attributes with two leading underscores and no more than one trailing underscore. Consider the following example of the updated
Dog
class — for simplicity, we omit other attributes that we defined previously.
>>> class Dog: ... def __init__(self, breed, name): ... self.breed = breed ... self.name = name ... self.__tag = f"{name} | {breed}" ... >>> dog = Dog("Rottweiler", "Ada") >>> dog.name 'Ada' >>> dog.__tag Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Dog' object has no attribute '__tag'
实际上python没有私有属性,只不过给这种属性的名称做了变化。
After the above update, the
Dog
instances will have a private attribute calledtag
, as indicated by its name. The instance object could still access its other attributes (e.g.,name
) as before. However, the instance couldn’t access the private attribute__tag
as we may be expecting. Indeed, such a restriction of accessing these attributes is exactly how they can be called “private” attributes. But how did it happen, under the hood? After all, I previously mentioned that all Python attributes are public by default. The following shows you how Python implements “private” attributes.
>>> dog.__dict__ {'breed': 'Rottweiler', 'name': 'Ada', '_Dog__tag': 'Ada | Rottweiler'} >>> dog._Dog__tag 'Ada | Rottweiler'
Protected Attributes
保护属性, 为前缀只有一个下划线的属性。
_xx 的属性名没有被变化,可以被直接引用到。
意思为,可以使用,但是不鼓励。
In the last section, we talked about private attributes, but how about protected attributes? The counterparts of protected attributes in Python are attributes whose names have only one underscore. Unlike the double underscores which will result in mangling, the single underscore prefix doesn’t change how Python interpreter handles these attributes — It’s merely a convention in the Python programming world to denote that they (i.e., the coder) doesn’t want you to access these attributes. However, again, if you insist on accessing them, you can still do that. Let’s see the code below.
>>> class Dog: ... def __init__(self, breed, name): ... self.breed = breed ... self.name = name ... self.__tag = f"{name} | {breed}" ... self._nickname = name[0]
We updated the class
Dog
by creating an instance attribute called_nickname
. As indicated by its name using an underscore prefix, it is considered as a “protected” attribute by convention. We can still access these protected attributes as other “public” attributes, but some IDEs or Python editors will not provide prompts (e.g., autocompletion hints) for these non-public attributes. See the screenshot for these examples using the Jupyter Notebook.No Auto-completion Suggestions for Non-Public Attributes
If we work with modules instead of classes as we’re doing here, when we import the module using
from the_module import *
, the names with the underscore prefix won’t be imported, providing a mechanism for restricting access to these “protected” attributes.
Properties
property跟属性还是有差异的
property是属性的子集, 其仅仅是函数。
property提供的注解, 提供了对私有属性的读取控制, 和 写保护。
Some OOP languages use attributes and properties interchangeably, but they are different concepts in Python. As discussed, attributes in Python are a broad concept that can include different kinds of attributes and functions. However, properties in Python are essentially functions that are decorated with the
@property
decorator. The decorated function will provide the callers with easier access to these properties. They can often be used together with the protected attributes. Let’s see an example.
>>> class Dog: ... # Same __init__ method ... @property ... def nickname(self): ... return self._nickname ... ... @nickname.setter ... def nickname(self, new_nickname): ... self._nickname = new_nickname ...
属性寻找与继承
属性寻找,遵循从下到上原则。
先在实例上自身找, 找不到寻找其上级class对应的属性, 在上级class找不到,则继续上上找, 知道找到object
属性动态添加
https://stackoverflow.com/questions/65547821/how-to-add-attribute-to-class-in-python
属性动态添加有两种方法(对class或者实例,都可以)
一, 使用setattr赋值
二,直接赋值, 例如 x.d = 1
1There're two ways of setting an attribute to your class;
First, by using setattr(class, variable, value)
Code Syntax
setattr(A,'c', 'c') print(dir(A))
OUTPUT
You can see the structure of the class A within attributes
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'c'] [Program finished]
Second, you can do it simply by assigning the variable
Code Syntax
A.d = 'd' print(dir(A))
OUTPUT
You can see the structure of the class A within attributes
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'c', 'd'] [Program finished]
缺点:
对于不存在的属性访问,会抛出异常。
这往往不是我们希望的。
Property解决此问题
很多库使用了 property装饰器, 来解决此问题。
但是使用这种方法需要注意, 不能使用 hasattr 再进行属性存在性的判断, 因为这个方法会导致属性一直存在。
https://www.datacamp.com/tutorial/property-getters-setters#:~:text=%40property%20is%20used%20to%20get%20the%20value%20of,We%20have%20to%20use%20it%20as%20a%20setter.?msclkid=16516808cf7311ec83af64cf4afa2afc
class Property: def __init__(self, var): ## initializing the attribute self.a = var @property def a(self): return self.__a ## the attribute name and the method name must be same which is used to set the value for the attribute @a.setter def a(self, var): if var > 0 and var % 2 == 0: self.__a = var else: self.__a = 2
property() 函数
https://www.geeksforgeeks.org/getter-and-setter-in-python/?msclkid=c78369f8cf8911eca494095d9ff7cc41
Using property() function to achieve getters and setters behaviour
In Python
property()
is a built-in function that creates and returns a property object. A property object has three methods, getter(), setter(), and delete().property()
function in Python has four argumentsproperty(fget, fset, fdel, doc)
,fget
is a function for retrieving an attribute value.fset
is a function for setting an attribute value.fdel
is a function for deleting an attribute value.doc
creates a docstring for attribute. A property object has three methods,getter()
,setter()
, anddelete()
to specifyfget
,fset
andfdel
individually. For Example
# Python program showing a # use of property() function class Geeks: def __init__(self): self._age = 0 # function to get value of _age def get_age(self): print("getter method called") return self._age # function to set value of _age def set_age(self, a): print("setter method called") self._age = a # function to delete _age attribute def del_age(self): del self._age age = property(get_age, set_age, del_age) mark = Geeks() mark.age = 10 print(mark.age)