当前位置: 首页 > 科技观察

Python中高阶概念的属性:你应该知道的五件事_0

时间:2023-03-17 13:46:14 科技观察

前言在现代编程世界中,面向对象编程(OOP)语言在改变软件开发中的设计和实现模式方面发挥了进化作用。Python作为OOP家族的重要成员,在过去10年左右的时间里逐渐流行起来。与其他OOP语言一样,Python围绕大量不同的对象(包括模块、类和函数)操作其数据。如果您有任何OOP语言的编程经验,您应该知道所有对象都有其内部特征数据,称为字段、属性或特性。在Python中,这些对象绑定的特征数据通常被称为属性。在本文中,我将在自定义类的上下文中具体讨论它们1.类属性我们经常需要创建自定义类,以便更好地管理我们项目中的数据。在Python中,类也是对象,这意味着它们可以有自己的属性。让我们看一个例子。>>>classDog:...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']如上所示,我们声明了一个名为Dog的类。因为所有的狗都属于Canisgenus和Canidaefamily,所以我们创建了两个名为Genus和Family的类属性来存储这两条信息。如您所见,我们可以直接使用类来访问这些属性。我们可以使用函数dir来显示狗的属性列表,其中包括科和属。这些在类级别定义的属性称为类属性,类可以直接检索它们。但是,与其他OOP语言不同,Python中的实例对象也可以直接访问这些类属性,如以下代码片段所示。>>>Dog().genus'Canis''>>>Dog().family'Canidae'2.实例属性通过自定义类,我们还可以为实例对象设置属性。这些属性称为实例属性,这意味着它们是特定于实例的数据。让我们转到狗类别。>>>classDog:...genus="Canis"...family="Canidae"...def__init__(self,breed,name):...self.breed=breed...self.name=name。..在上面的代码中,我们定义了__init__函数,它将作为构造函数来创建一个新的Dog实例。第一个参数self指的是我们正在创建的实例。在实例化(即创建一个新的实例)时,我们会将品种和名称信息赋给新的实例对象,这些属性将成为实例特征的一部分,如下图。>>>dog=Dog("Rottweiler","Ada")>>>dog.name'Ada'>>>dog.breed'Rottweiler'需要注意的一点是我们可以拥有与类属性Instance相同的属性任务。在这种情况下,当您检索实例的此属性时,将不会检索类属性。换句话说,当您使用实例对象检索类属性时,Python将首先检查实例本身是否具有同名的属性集。如果没有,Python将使用class属性作为后备。此外,设置实例的属性不会影响同名类的属性。让我们在下面的代码片段中看一下这些特征。>>>dog.genus="Felis">>>dog.genus'Felis'>>>Dog('Poodle','Cutie').genus'Canis'3.作为属性的函数在Python中,一切都是对象,前面我提到类是对象。此外,函数是Python对象。在类中,我们可以定义函数,通常称为方法。根据这些函数的使用方式,我们可以进一步将它们分为类方法、静态方法和实例方法。在这里没有必要了解这些差异。虽然一些OOP语言将属性(或属性)和函数视为不同的实体,但Python将这些方法(函数)视为类的属性——与我们之前定义的类属性没有太大区别。让我们用上面提到的三种方法更新Dog类:类方法、静态方法和实例方法,如下所示。>>>classDog:...genus="Canis"...family="Canidae"...def__init__(self,breed,name):...self.breed=breed...self.name=name。..@classmethod...defrom_tag(cls,tag_info):...breed=tag_info["breed"]...name=tag_info["name"]...returncls(breed,name)...@staticmethod...defcan_bark():...print("Yes.Alldogscanbark.")...defbark(self):...print("Thedogisbarking.")。..对于更新的类,我们可以使用函数dir来检查类的属性列表。类方法和静态方法都包含在列表中,如下所示。>>>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']但是,有一点可能会让一些人感到意外,就是在这个列表中包含了实例方法吠。我们知道实例方法就是实例对象调用的那些函数,所以有些人可能会认为这些实例方法应该绑定到所有的个体实例上。然而,在Python中情况并非如此。在解释实例方法如何工作之前,让我们看一下下面的代码。>>>dog=Dog("Rottweiler","Ada")>>>dog.bark()Thedog正在吠叫。>>>Dog.bark(dog)Thedog正在吠叫。如上所示,我们首先创建Dog类的一个实例。和其他OOP语言一样,实例对象可以直接调用实例方法bark。但是,Python与其他语言的不同之处在于,实例方法调用是通过类进行操作的,通过将实例作为参数传递来调用定义的函数(即dog.bark(dog))。也就是说,instance.inst_method()与Python中的Class.inst_method(instance)本质上是一样的。这是可能的,因为Dog类“拥有”实例方法,这是一种内存节省机制,因为Python不需要为每个实例对象创建一个单独的函数副本。相反,当实例调用实例方法时,Python将调用委托给类,类将通过传递实例来调用相应的函数(它会在定义的函数中设置为self参数)。4.私有属性如果你有过OOP经验,应该不会对public、private、protected等访问修饰符的存在感到陌生。这些修饰符限制了可以访问修改后的属性和函数的范围。然而,你很少在Python中听到这样的讨论。事实上,如果我们借用OOP的术语,所有Python属性都是公共的。如上所示,在类和实例可访问的地方,类和实例属性都可以自由访问。因此,严格来说,Python中没有真正私有或受保护的属性(稍后讨论)。我们只是类比使用这些术语,以便来自其他OOP背景的程序员可以更容易地理解相关的编码约定(是的,只是一个约定,并不作为真正的访问控制强制执行)。让我们首先讨论如何在Python中定义“私有”属性。约定是用两个前导下划线和不超过一个尾随下划线命名这些属性。考虑下面更新的Dog类的示例——为简单起见,我们省略了之前定义的其他属性。>>>classDog:...def__init__(self,breed,name):...self.breed=breed...self.name=name...self.__tag=f"{name}|{breed}"...>>>dog=Dog("Rottweiler","Ada")>>>dog.name'Ada'>>>dog.__tagTraceback(mostrecentcallast):File"",line1,inAttributeError:'Dog'objecthasnoattribute'__tag'在上面的更新之后,Dog实例将有一个名为tag的私有属性,顾名思义。实例对象仍然可以像以前一样访问它的其他属性(例如,名称)。但是,该实例无法访问保留在标记中的私有属性,这可能是我们所期望的。事实上,这种对访问这些属性的限制就是将它们称为“私有”属性的原因。但它是如何发生的,在引擎盖下?毕竟,我之前提到过所有Python属性在默认情况下都是公共的。下面将向您展示Python是如何实现“私有”属性的。>>>dog.__dict__{'breed':'Rottweiler','name':'Ada','_Dog__tag':'Ada|Rottweiler'}>>>dog._Dog__tag'Ada|Rottweiler'__dict__特殊方法(也叫dunder方法,名称前后有双下划线)可以显示对象的字典表示。具体来说,字典中的键值对是对象的属性及其值。我们可以看到,除了bread和name属性之外,还有一个名为_dog__tag标签的属性。该属性正是私有属性__tag通过称为mangling的过程与对象相关联的方式。具体来说,重整或名称重整是在私有属性前加上_ClassName前缀,以便我们人为地创建对这些“私有”属性的访问限制。但是,如果我们确实想要检索任何私有属性,我们仍然可以使用经过修饰的名称访问它,就像我们在代码段中使用_dog__标记所做的那样。5.受保护的属性在上一节中,我们讨论了私有属性,那么受保护的属性呢?Python中一个受保护的属性对应的属性名只有一个下划线。与导致混淆的双下划线不同,单个下划线前缀不会改变Python解释器处理这些属性的方式——这只是Python编程世界中的一种约定,表明他们(例如,编码人员)不希望您访问到这些属性。但是,如果您坚持访问它们,您仍然可以这样做。让我们看看下面的代码。>>>classDog:...def__init__(self,breed,name):...self.breed=breed...self.name=name...self.__tag=f"{name}|{breed}"...self._nickname=name[0]我们通过创建名为_nickname的实例属性来更新类Dog。按照惯例,它被认为是一个“受保护”的属性,如其名称前加下划线所示。我们仍然可以像访问其他“公共”属性一样访问这些受保护的属性,但是某些IDE或Python编辑器不为这些非公共属性提供提示(例如自动完成提示)。使用JupyterNotebooks查看这些示例的屏幕截图。如果我们使用模块而不是类,就像我们在这里所做的那样,当我们使用from_moduleimport*导入模块时,将不会导入带有下划线前缀的名称,从而提供一种机制来限制对这些“受保护”属性的访问。