python基础知识之类class

Python中的类(Class)是面向对象编程(OOP)的核心概念之一。它为创建对象提供了一种蓝图或模板。类提供了一种将数据和功能捆绑在一起的手段。创建一个新类会创建一个新类型的对象,允许创建该类型的新实例。每个类实例都可以附加属性来维持其状态。类实例也可以有方法(由其类定义)来修改其状态。

类的定义

定义类非常简单,使用class关键字进行定义:

class ClassName:
	pass

在对类进行命名时,建议使用驼峰命名法,即当多个单词组合在一起时,每个单词的首字母大写。(在命名变量时,通常使用小写字母,并使用下划线作为单词之间的分隔符)类的命名应该与其功能相关,即具有一定的描述性。另外,除非常见术语,一般不建议缩写。

类属性与类实例(class objects and instance objects)

类属性

类属性是附加到类上的变量,它们被该类的所有实例共享。这意味着当更改类属性时,这个更改会反映在该类的所有实例上。比如:

class MyClass:
    class_attribute = "This is a class attribute"

print(MyClass.class_attribute)  # 输出:"This is a class attribute"

对于所有的类的实例,上例中的class_attribute属性都一致的。

类实例(类的初始化)

类实例是根据类的定义创建的对象。每个实例都有自己的属性和方法,并且与其他实例独立。实例属性是附加到类实例上的变量。特别的,python类提供了self关键字用于区分实例属性还是类属性。 比如:

class MyClass:
    class_attribute = "This is a class attribute"

    def __init__(self, name):
        self.instance_attribute = name

# 创建类的实例
obj1 = MyClass("Instance 1") # 调用def __init__()初始化类
obj2 = MyClass("Instance 2")

print(obj1.class_attribute)        # 输出:"This is a class attribute"
print(obj1.instance_attribute)     # 输出:"Instance 1"
print(obj2.instance_attribute)     # 输出:"Instance 2"

带有self的,全部为类实例的属性。在程序中,如果obj1的类实例属性,这不会影响obj2的实例属性。但是,如果在任何一个实例中修改类属性,即示例中的class_attribute,所有示例中的该属性都会被修改。例如:

obj1.class_attribute = "Modified through instance"
print(obj1.class_attribute)        # 输出:"Modified through instance"
print(obj2.class_attribute)        # 输出:"Modified class attribute"
print(MyClass.class_attribute)     # 输出:"Modified class attribute"

类方法

类的方法一般指类所包含的函数,但是值得注意的是,一个类可以没有函数,充当一个数据容器,类似于字典。在构建类的方法时,所有函数都要以self作为函数的第一个‘变量’,以说明该函数为类的函数。下面给出一个例子:

class Example:
    def __init__(self, value):
        self.attribute = value
    
    def display(self):
        print(self.attribute)
    
    @staticmethod
    def show():
    	print('staticmethod')

函数__init__是类的内置函数,用于初始化一个类。一个类中所有的数据变量都应该在该函数中进行初始化。display函数是类的函数,可以访问类的数据并执行特定操作。

example=Example('test')
example.display() # 调用display 函数,输出'test'

如果在类的方法中不使用self作为第一个变量,那么当该函数调用类实例的数据时,将会报错。如果一个函数不需要调用类中的数据,或者想不创建实例就调用该方法时,可以使用staticmethod方法。如上例中的show方法。

example.show() # 输出为'staticmethod'
Example.show() # 输出为'staticmethod'

类的访问控制

在面向对象的编程中,访问控制指的是对对象的属性和方法的访问权限的管理。正确的访问控制可以保证对象的状态不会被外部不恰当地修改,从而确保数据的完整性和安全性。Python 并没有像 Java 或 C++ 那样的严格访问修饰符(如 privateprotectedpublic)。但 Python 通过命名约定和一些特性提供了访问控制的机制。

  1. 公开(Public)

    • 这是默认的访问控制级别。
    • 属性或方法的名称没有任何前导下划线。
    • 可以从类的任何地方、类的任何实例或类的任何子类进行访问。
    • 示例:name
  2. 受保护(Protected)

    • 通过一个前导下划线表示(例如 _name)。
    • 通常意味着它只应该在该类和其子类中使用。
    • 但需要注意的是,这只是一个约定。Python 不会真正阻止你在类的外部访问这些属性或方法。
    • 示例:_protected_attribute
  3. 私有(Private)

    • 通过两个前导下划线表示(例如 __name)。
    • Python 会对这些属性或方法进行名称修饰,这使得它们不能从外部直接访问。
    • 名称修饰涉及到在前面加上_classname(其中 classname 是当前的类名)。
    • 示例:__private_attribute
    • 虽然可以通过名称修饰来访问私有属性,但这是不推荐的,因为这违反了封装的原则。
class MyClass:
    def __init__(self):
        self.public_attribute = "I'm a public attribute"
        self._protected_attribute = "I'm a protected attribute"
        self.__private_attribute = "I'm a private attribute"

    def get_private_attribute(self):
        return self.__private_attribute

obj = MyClass()

print(obj.public_attribute)         # 正常访问
print(obj._protected_attribute)     # 可以访问,但不建议在类外部这样做
# print(obj.__private_attribute)    # 报错:AttributeError
print(obj.get_private_attribute())  # 通过公开方法访问私有属性
print(obj._MyClass__private_attribute)  # 不建议,但可以通过名称修饰访问

Python 提供的访问控制主要是基于约定和程序员的自律,而不是语言层面的强制性规则。尽管如此,理解和正确使用这些机制是编写健壮和可维护的代码的关键。

类的继承与多态

在面向对象的编程中,继承多态是两个核心概念,它们允许程序员编写更加模块化、可扩展和可维护的代码。

类的继承

继承是一种机制,允许一个类(子类/派生类)继承另一个类(父类/基类)的属性和方法。

  • 代码复用:子类可以复用父类的代码,这减少了重复的代码。
  • 扩展:子类可以扩展或修改它从父类继承的行为。

示例:

# 定义一个基类
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        pass
    
    def get_name(self):
    	print(self.name)

# 定义一个派生类
class Dog(Animal):
    def speak(self):
        return f"{self.name} says Woof!"

class Cat(Animal):
    def speak(self):
        return f"{self.name} says Meow!"

dog = Dog("Buddy")
print(dog.speak())  # 输出:"Buddy says Woof!"
dog.get_name() # 输出“Buddy”

在上述例子中,子类DogCat继承了Animal,所以Animal的数据变量和函数都被继承了下来。但是,由于子类DogCat重写了speak函数,所以在子类的实例调用speak函数时,都将调用之类的speak函数,而非Animalspeak函数。

类的多(重)继承

顾名思义,类的多继承是指一个类可以从多个父类继承属性和方法。使用多继承的优缺点如下:

  • 优点:
    • 可以用于创建一个集成了多个类功能的新类。
    • 有助于代码复用,因为可以从多个源继承行为和属性。
  • 缺点:
    • 菱形问题(Diamond Problem):当两个父类都继承自一个祖先类,并且一个子类同时继承这两个父类时,可能会出现歧义。如果两个父类提供了相同的方法,子类可能会混淆它应该继承哪个方法。但是按照python内部的继承顺序可以解决这一缺点,难处在与用户是否可以正确理解并使用该顺序。
    • 可能导致代码的复杂性增加,使其更难理解和维护。
class A:
    def method(self):
        print("A method")

class B(A):
    def method(self):
        print("B method")

class C(A):
    def method(self):
        print("C method")

class D(B, C):
    pass

d = D()
d.method() # “B method"

在使用多继承时,python是使用的Method Resolution Order, MRO机制确定继承顺序。Method Resolution Order (MRO) 是 Python 中用于支持多重继承的一个机制。当一个类是从多个父类继承过来的,MRO 确定了当调用一个方法时应该从哪个父类继承这个方法。Python 中的 MRO 遵循三个主要的原则:

  1. 子类优先于父类:如果子类覆盖了某个方法或属性,那么它应该在父类之前被考虑。
  2. 多个父类会按照它们在列表中的顺序被考虑:也就是说,如果有多个父类,那么第一个父类的方法或属性将首先被考虑,然后是第二个,依此类推。
  3. 一旦确定了父类的顺序,这个顺序应该是一致的,不应该发生变化

为了确定类的 MRO,Python 使用了一个叫做 C3 线性化的算法。这个算法确保所有的子类都会在它们的父类之前被考虑,并保证父类的顺序与它们在代码中的定义顺序一致。
考虑以下的类结构:

class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass

对于 D 类的 MRO,它首先会考虑 D 本身,然后是它的第一个父类 B,然后是 B 的父类 A,然后才是第二个父类 C,然后是 C 的父类(但由于 A 已经被考虑过了,所以它不会再次出现)。因此,D 的 MRO 将是:D -> B -> C -> A

如何查看 MRO

  1. 使用 mro() 方法:

    print(D.mro())#[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
    
  2. 使用内置的 help() 函数:

    help(D)
    

    在输出的顶部,你会看到 “Method resolution order” 部分,它列出了 MRO。

MRO 的问题可能在多重继承中变得复杂,尤其是当有多个类与相同的父类有关系时。这就是为什么,在设计面向对象的应用程序时,多重继承应该谨慎使用。如果不小心,可能会遇到难以预测的行为和“菱形继承”的问题。

类的多态

多态是面向对象编程中的四大基本特性之一,其他三个特性是封装、继承和抽象。多态来自于希葛亚文中的"poly"(意为"多")和"morph"(意为"形状"或"形态"),所以字面上,它意味着"多种形态"。

在面向对象编程中,多态的概念是指:使用一个接口来代表多种形式的数据。更具体地说,多态允许我们定义在多个类中的方法,这些方法具有相同的名称但具有不同的实现。多态具有以下特点:

  1. 接口的统一:多态为不同的对象提供了一个统一的接口。这意味着我们可以使用相同的方法名来调用不同对象的方法。

  2. 动态绑定:在运行时,程序会根据对象的实际类型来决定调用哪个类中的方法。

Python 是动态类型语言,这意味着多态是其核心的一部分。在 Python 中,多态主要通过鸭子类型实现,这意味着我们不关心对象是什么类型,只关心它能做什么。让我们考虑一个简单的例子,其中有几种不同的动物,每种动物都有自己的声音。

class Dog:
    def sound(self):
        return "Woof!"

class Cat:
    def sound(self):
        return "Meow!"

class Bird:
    def sound(self):
        return "Chirp!"

虽然每个类都有一个 sound 方法,但实现方式各不相同。现在,我们可以编写一个函数来展示多态的行为:

def animal_sound(animal):
    return animal.sound()

d = Dog()
c = Cat()
b = Bird()

print(animal_sound(d))  # 输出: Woof!
print(animal_sound(c))  # 输出: Meow!
print(animal_sound(b))  # 输出: Chirp!

在上面的示例中,animal_sound 函数不关心其参数的数据类型,只要该参数具有一个名为 sound 的方法。这就是多态的魔力。利用好多态,可以:

  1. 代码重用:你可以创建统一的接口,然后让多个类实现该接口,而不必为每个类编写单独的功能。

  2. 扩展性:添加新的类或方法非常容易,而不需要修改已有的代码。

  3. 灵活性:多态允许程序在运行时动态地确定特定的方法和对象,这为开发提供了很大的灵活性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值