Python之面向对象的哪些事

1.面向对象,类,类的方法,类的属性,实例化,父类,子类,继承

  • 面向对象:面向对象是一种编程范式,它把程序中的数据和操作数据的方法组织成一些相互关联的对象,这样可以更容易地模拟现实世界中的事物和行为。比如,你可以把一只猫看作一个对象,它有一些属性(如颜色,名字,年龄)和一些方法(如吃,睡,叫)。
  • 类:类是一种定义对象的模板,它描述了对象的属性和方法。你可以把类看作是一个制造对象的工厂,它可以根据自己的设计来创建不同的对象。比如,你可以定义一个Cat类,它包含了猫的属性和方法。
  • 类的方法:类的方法是一种定义在类中的函数,它可以操作类或者类的实例的属性或者其他数据。类的方法通常用self作为第一个参数,表示当前的类或者实例。比如,你可以定义一个Cat类的方法叫make_sound,它可以让猫发出叫声。
  • 类的属性:类的属性是一种定义在类中的变量,它可以存储类或者类的实例的数据。类的属性分为两种:类属性和实例属性。类属性是属于整个类的属性,它可以被所有的类的实例共享。实例属性是属于每个类的实例的属性,它可以被每个实例单独修改。比如,你可以定义一个Cat类的类属性叫species,它表示猫的物种;你也可以定义一个Cat类的实例属性叫name,它表示每只猫的名字。
  • 实例化:实例化是一种创建对象的过程,它根据类的定义来生成一个具体的对象,并赋予相应的属性和方法。你可以把实例化看作是从工厂中取出一个产品,并给它贴上标签。比如,你可以根据Cat类来实例化一个对象叫tommy,它是一只黑色的猫。
  • 继承:继承是一种创建新类的方式,它可以让新类继承已有类的属性和方法,并添加或修改一些新的特征。你可以把继承看作是在原有产品的基础上进行改进或定制。比如,你可以定义一个新类叫Lion,它继承了Cat类,并添加了一个新属性叫mane,表示狮子是否有鬃毛。
  • 父类:父类是一种已有的类,它可以被其他类继承。父类也叫做基类或超类。比如,在上面的例子中,Cat就是Lion的父类。
  • 子类:子类是一种新创建的类,它继承了父类的属性和方法,并添加或修改了一些新特征。子类也叫做派生类或子级。比如,在上面的例子中,Lion就是Cat的子类。

下面是一个用python代码来展示这些概念和关系的例子:

# 定义Cat类
class Cat:
    # 定义Cat类的类属性
    species = "Felis catus"

    # 定义Cat类的初始化方法
    def __init__(self, name, color, age):
        # 定义Cat类的实例属性
        self.name = name
        self.color = color
        self.age = age

    # 定义Cat类的方法
    def make_sound(self):
        print(f"{self.name} says: Meow!")

# 实例化Cat类
tommy = Cat("Tommy", "black", 3)
lily = Cat("Lily", "white", 2)

# 调用Cat类的属性和方法
print(tommy.species) # 输出 Felis catus
print(lily.name) # 输出 Lily
tommy.make_sound() # 输出 Tommy says: Meow!

# 定义Lion类,继承Cat类
class Lion(Cat):
    # 定义Lion类的初始化方法
    def __init__(self, name, color, age, mane):
        # 调用父类的初始化方法
        super().__init__(name, color, age)
        # 定义Lion类的新属性
        self.mane = mane

    # 重写父类的方法
    def make_sound(self):
        print(f"{self.name} says: Roar!")

# 实例化Lion类
simba = Lion("Simba", "golden", 4, True)

# 调用Lion类的属性和方法
print(simba.species) # 输出 Felis catus
print(simba.mane) # 输出 True
simba.make_sound() # 输出 Simba says: Roar!

2.类中的方法和普通函数的定义和调用的异同:

  • 定义的位置不同:类中的方法必须写在类的内部,而普通函数可以写在任何地方,不需要属于某个类。
  • 定义的方式不同:类中的方法必须至少有一个参数,通常是self,表示当前的类或实例对象。普通函数可以没有参数,也可以有任意个参数。
  • 调用的方式不同:类中的方法必须通过类名或实例对象来调用,而普通函数可以直接调用。类中的方法在调用时不需要传递self参数,而普通函数需要传递所有参数。
  • 调用的效果不同:类中的方法可以访问和修改类或实例对象的属性和数据,而普通函数只能操作传入的参数或全局变量。

下面是一个简单的例子来说明这些异同:

# 定义一个普通函数
def add(x, y):
    return x + y

# 定义一个类
class Calculator:
    # 定义一个类属性
    name = "Calculator"

    # 定义一个初始化方法
    def __init__(self, x, y):
        # 定义两个实例属性
        self.x = x
        self.y = y

    # 定义一个类中的方法
    def add(self):
        return self.x + self.y

# 调用普通函数
print(add(1, 2)) # 输出 3

# 调用类中的方法
cal = Calculator(1, 2) # 创建一个实例对象
print(cal.add()) # 输出 3
print(Calculator.add(cal)) # 输出 3

 3.类中的方法的第一个参数可以不是self吗

类中的方法的第一个参数可以不是self吗?答案是可以,但是不推荐这样做。因为self是一种约定俗成的命名方式,它表示当前的类或实例对象,它可以让代码更清晰和一致。如果你使用其他的名字,比如this或者obj,可能会造成混淆或者错误。

在python中,方法和函数都是对象,它们可以作为参数传递给其他方法或函数。当你定义一个类中的方法时,你实际上是定义了一个函数对象,并把它绑定到类的名称空间中。当你创建一个类的实例时,你实际上是创建了一个新的对象,并把它绑定到实例的名称空间中。当你通过实例对象来调用类中的方法时,你实际上是把实例对象作为第一个参数传递给了方法。

这个过程是由python解释器自动完成的,你不需要显式地写出第一个参数。但是你需要在定义方法时指定第一个参数的名字,这个名字就是self。当然,你也可以使用其他的名字,但是这样做会违反python的编码风格和习惯。

4.定义一个类的方法时,第一个参数永远是self,调用时不需要传self

当你通过实例对象来调用类中的方法时,实例对象的引用就会自动作为第一个参数传递给方法,这个参数就是self。self表示当前的类或实例对象,它可以让方法访问和操作对象的属性和数据。

你可以把self看作是一个变量,它的值就是实例对象的引用。当你定义一个类中的方法时,你需要在参数列表中指定self,但是当你调用这个方法时,你不需要传递self,因为python解释器会自动把实例对象的引用赋值给self。

下面是一个简单的例子来说明这个过程:

# 定义一个类
class Person:
    # 定义一个初始化方法
    def __init__(self, name, age):
        # 定义两个实例属性
        self.name = name
        self.age = age

    # 定义一个类中的方法
    def say_hello(self):
        print(f"Hello, I am {self.name}, and I am {self.age} years old.")

# 创建一个实例对象
p = Person("Alice", 20)

# 调用类中的方法
p.say_hello() # 输出 Hello, I am Alice, and I am 20 years old.

# 等价于
Person.say_hello(p) # 输出 Hello, I am Alice, and I am 20 years old.

5.在类中,哪些参数,方法或者属性需要放在“ def__init__(self)”里面?

  • 参数:init 方法的第一个参数必须是self,它表示当前的类或实例对象。除此之外,init 方法可以有任意个参数,它们可以用来初始化对象的属性或者执行其他操作。当你创建一个类的实例对象时,你需要传递相应的参数给__init__ 方法,除了self之外。比如,如果你定义了一个Person类,它的__init__ 方法有三个参数:self, name, age。那么当你创建一个Person类的实例对象时,你需要传递两个参数给__init__ 方法:name, age。例如:p = Person(“Alice”, 20)。
  • 方法:init 方法本身就是一个类中的方法,它用来初始化对象的属性或者执行其他操作。除了__init__ 方法之外,类中还可以有其他的方法,它们可以用来定义对象的行为或者功能。这些方法不需要放在__init__ 方法里面,而是放在类的外面,和__init__ 方法平级。比如,如果你定义了一个Person类,它有一个say_hello方法,它用来打印对象的名字和年龄。那么这个方法不需要放在__init__ 方法里面,而是放在类的外面,和__init__ 方法平级。例如:
# 定义一个Person类
class Person:
    # 定义一个初始化方法
    def __init__(self, name, age):
        # 定义两个实例属性
        self.name = name
        self.age = age

    # 定义一个say_hello方法
    def say_hello(self):
        print(f"Hello, I am {self.name}, and I am {self.age} years old.")
  • 6.属性分为两种:类属性和实例属性

  • 属性:属性是一种定义在类中的变量,它可以存储类或者类的实例的数据。属性分为两种:类属性和实例属性。类属性是属于整个类的属性,它可以被所有的类的实例共享。实例属性是属于每个类的实例的属性,它可以被每个实例单独修改。一般来说,类属性不需要放在__init__ 方法里面,而是放在类的外面,和方法平级。实例属性需要放在__init__ 方法里面,并且通过self参数来赋值。比如,如果你定义了一个Person类,它有一个类属性叫species,它表示人类的物种;它也有两个实例属性叫name和age,它们表示每个人的名字和年龄。那么这些属性应该这样定义:
# 定义一个Person类
class Person:
    # 定义一个类属性
    species = "Homo sapiens"

    # 定义一个初始化方法
    def __init__(self, name, age):
        # 定义两个实例属性
        self.name = name
        self.age = age

    # 定义一个通过实例对象来调用的方法
    def say_hello(self):
        print(f"Hello, I am {self.name}, and I belong to {self.species}.")

    # 定义一个通过类名来调用的方法
    @classmethod
    def say_species(cls):
        print(f"We are {cls.species}.")

# 创建两个Person类的实例对象
p1 = Person("Alice", 20)
p2 = Person("Bob", 25)

# 调用两个通过实例对象来调用的方法
p1.say_hello() # 输出 Hello, I am Alice, and I belong to Homo sapiens.
p2.say_hello() # 输出 Hello, I am Bob, and I belong to Homo sapiens.

# 调用两个通过类名来调用的方法
Person.say_species() # 输出 We are Homo sapiens.

7.类中一定要有“def __init__”这个方法吗

类中一定要有“def init”这个方法吗?答案是不一定。init 方法是一种特殊的方法,它用来初始化对象的属性或者执行其他操作。如果你的类不需要做任何初始化的工作,或者你想用其他的方法来做初始化的工作,那么你可以不定义__init__ 方法。

但是,如果你不定义__init__ 方法,那么python解释器会自动为你的类提供一个默认的__init__ 方法,它什么也不做,只是接受self参数,并且返回一个空的对象。 这样,你就不能给你的对象赋予任何属性或者数据,也不能执行任何操作。这可能会导致你的对象缺乏功能或者意义。

因此,一般来说,推荐你为你的类定义一个__init__ 方法,除非你有特殊的理由不这样做。这样,你可以根据你的类的设计和需求来初始化你的对象的属性或者数据,或者执行其他操作。这样,你可以让你的对象更有功能和意义。

你想看一个对比的例子,来说明有和没有__init__ 方法的区别吗?好的,我可以给你一个例子,但是我要提醒你,有__init__ 方法的类通常是一个更好的编程习惯,因为它可以让你的对象有初始化的功能和意义。如果你想创建一个没有功能和意义的对象,你可以不定义__init__ 方法,或者使用其他的方法来初始化你的对象。

下面是一个对比的例子,来说明有和没有__init__ 方法的区别:

# 定义一个没有__init__ 方法的类
class Rectangle1:
    # 定义一个计算面积的方法
    def area(self):
        return self.length * self.width

    # 定义一个计算周长的方法
    def perimeter(self):
        return 2 * (self.length + self.width)

# 定义一个有__init__ 方法的类
class Rectangle2:
    # 定义一个初始化方法
    def __init__(self, length, width):
        # 定义两个实例属性
        self.length = length
        self.width = width

    # 定义一个计算面积的方法
    def area(self):
        return self.length * self.width

    # 定义一个计算周长的方法
    def perimeter(self):
        return 2 * (self.length + self.width)

# 创建两个没有__init__ 方法的类的实例对象
r1 = Rectangle1()
r2 = Rectangle1()

# 创建两个有__init__ 方法的类的实例对象
r3 = Rectangle2(10, 5)
r4 = Rectangle2(15, 8)

# 给两个没有__init__ 方法的类的实例对象赋予属性
r1.length = 10
r1.width = 5
r2.length = 15
r2.width = 8

# 调用两个没有__init__ 方法的类的实例对象的方法
print(r1.area()) # 输出 50
print(r1.perimeter()) # 输出 30
print(r2.area()) # 输出 120
print(r2.perimeter()) # 输出 46

# 调用两个有__init__ 方法的类的实例对象的方法
print(r3.area()) # 输出 50
print(r3.perimeter()) # 输出 30
print(r4.area()) # 输出 120
print(r4.perimeter()) # 输出 46

你可以看到,在这个例子中:

  • Rectangle1是一个没有__init__ 方法的类,它只有两个方法:area和perimeter,它们分别用来计算矩形的面积和周长。
  • Rectangle2是一个有__init__ 方法的类,它也有两个方法:area和perimeter,它们分别用来计算矩形的面积和周长。但是它还有一个初始化方法,它用来给对象赋予属性length和width,它们分别表示矩形的长度和宽度。
  • r1和r2是两个没有__init__ 方法的类的实例对象,它们没有任何属性或者数据。我们需要给它们赋予属性length和width,才能调用它们的方法area和perimeter。
  • r3和r4是两个有__init__ 方法的类的实例对象,它们已经有了属性length和width,并且根据传入的参数来初始化这些属性。我们不需要再给它们赋予属性length和width,就可以直接调用它们的方法area和perimeter。

这个例子可以看出,有和没有__init__ 方法的区别主要在于:

  • 是否需要给对象赋予属性或者数据:有__init__ 方法的类可以在创建对象时就给对象赋予属性或者数据,而没有__init__ 方法的类需要在创建对象后才能给对象赋予属性或者数据。
  • 是否能保证对象有正确的属性或者数据:有__init__ 方法的类可以保证对象有正确的属性或者数据,因为它们是在初始化方法中赋值的,而没有__init__ 方法的类不能保证对象有正确的属性或者数据,因为它们是在外部赋值的,可能会出现错误或者缺失。
  • 是否能让对象有更多的功能或者意义:有__init__ 方法的类可以让对象有更多的功能或者意义,因为它们可以根据类的设计和需求来初始化对象的属性或者数据,或者执行其他操作,而没有__init__ 方法的类只能让对象有最基本的功能或者意义,因为它们只能根据默认的方式来创建空的对象。

8.类中私有或者公有变量,属性,方法的定义和调用方式

  • 我们可以通过在变量,属性,方法的名字前加上双下划线(__)来表示它们是私有的。这样,它们就不能被外部访问或修改,只能在类内部使用。这是一种实现数据封装和信息隐藏的技巧。
  • 但是,这种技巧并不完美,因为python会对双下划线开头的变量,属性,方法进行一种叫做名称修饰(name mangling)的处理。它会把这些变量,属性,方法的名字改成_类名__变量名的形式。这样,我们就可以通过这种形式来访问或修改私有成员。比如,我们可以通过a1._Animal__id来访问或修改a1对象的私有属性__id。
# 定义一个类
class Animal:
    # 定义一个公有类属性
    kingdom = "Animalia"

    # 定义一个私有类属性
    __count = 0

    # 定义一个公有初始化方法
    def __init__(self, name):
        # 定义一个公有实例属性
        self.name = name
        # 定义一个私有实例属性
        self.__id = Animal.__count
        # 调用一个私有类方法
        Animal.__increase_count()

    # 定义一个公有实例方法
    def introduce(self):
        print(f"Hi, I am {self.name}, and I belong to {self.kingdom}.")
        # 调用一个私有实例方法
        self.__show_id()   #可以先调用后定义

    # 定义一个私有实例方法
    def __show_id(self):
        print(f"My ID is {self.__id}.")

    # 定义一个公有类方法
    @classmethod
    def show_kingdom(cls):
        print(f"We are {cls.kingdom}.")
        # 调用一个私有类方法
        cls.__show_count()

    # 定义一个私有类方法
    @classmethod
    def __show_count(cls):
        print(f"There are {cls.__count} animals.")

    # 定义一个私有类方法
    @classmethod
    def __increase_count(cls):
        cls.__count += 1

# 创建两个Animal类的实例对象
a1 = Animal("Cat")
a2 = Animal("Dog")

# 调用两个公有实例方法
a1.introduce() 
# 输出 Hi, I am Cat, and I belong to Animalia.
# 输出 My ID is 0.
a2.introduce() 
# 输出 Hi, I am Dog, and I belong to Animalia.
# 输出 My ID is 1.

# 调用两个公有类方法
Animal.show_kingdom() 
# 输出 We are Animalia.
# 输出 There are 2 animals.

# 尝试访问或修改两个公有实例属性
print(a1.name) # 输出 Cat
print(a2.name) # 输出 Dog
a1.name = "Lion" # 修改成功
a2.name = "Wolf" # 修改成功

# 尝试访问或修改两个私有实例属性
try:
    print(a1.__id) # 报错 AttributeError: 'Animal' object has no attribute '__id'
except AttributeError as e:
    print(e)

try:
    print(a2.__id) # 报错 AttributeError: 'Animal' object has no attribute '__id'
except AttributeError as e:
    print(e)

try:
    a1.__id = 100 # 没有报错,但是没有修改成功,而是创建了一个新的公有属性__id
except Exception as e:
    print(e)

try:
    a2.__id = 200 # 没有报错,但是没有修改成功,而是创建了一个新的公有属性__id
except Exception as e:
    print(e)

# 尝试访问或修改两个公有类属性
print(Animal.kingdom) # 输出 Animalia
Animal.kingdom = "Mammalia" # 修改成功

# 尝试访问或修改两个私有类属性
try:
    print(Animal.__count) # 报错 AttributeError: type object 'Animal' has no attribute '__count'
except AttributeError as e:
    print(e)

try:
    Animal.__count = 100 # 没有报错,但是没有修改成功,而是创建了一个新的公有属性__count
except Exception as e:
    print(e)

# 尝试访问或修改两个私有实例方法
try:
    a1.__show_id() # 报错 AttributeError: 'Animal' object has no attribute '__show_id'
except AttributeError as e:
    print(e)

try:
    a2.__show_id() # 报错 AttributeError: 'Animal' object has no attribute '__show_id'
except AttributeError as e:
    print(e)

# 尝试访问或修改两个私有类方法
try:
    Animal.__show_count() # 报错 AttributeError: type object 'Animal' has no attribute '__show_count'
except AttributeError as e:
    print(e)

try:
    Animal.__increase_count() # 报错 AttributeError: type object 'Animal' has no attribute '__increase_count'
except AttributeError as e:
    print(e)

9.类中方法调用顺序及实例方法,类方法,和静态方法 的区别和使用场景

  • 实例方法在类中调用,可以先调用后定义,但是要注意以下几点:
    • 实例方法只能在其他实例方法中调用,不能在类方法或者静态方法中调用。
    • 实例方法只能通过self参数来调用,不能通过类名或者实例对象来调用。
    • 实例方法必须在创建类的实例对象之前定义,否则会报错AttributeError: ‘ClassName’ object has no attribute ‘method_name’。
  • 类方法在类中调用,可以先调用后定义,但是要注意以下几点:
    • 类方法可以在其他类方法或者静态方法中调用,但是不能在实例方法中调用。
    • 类方法可以通过cls参数或者类名来调用,但是不能通过实例对象来调用。
    • 类方法必须在创建类的实例对象之前定义,并且要使用@classmethod装饰器来标识,否则会报错TypeError: method_name() takes no arguments (1 given)。
  • 静态方法在类中调用,可以先调用后定义,但是要注意以下几点:
    • 静态方法可以在其他静态方法中调用,但是不能在实例方法或者类方法中调用。
    • 静态方法可以通过类名来调用,但是不能通过self参数或者实例对象来调用。
    • 静态方法必须在创建类的实例对象之前定义,并且要使用@staticmethod装饰器来标识,否则会报错TypeError: method_name() takes no arguments (1 given)。

实例方法,类方法,和静态方法的区别主要在于以下几个方面:

  • 定义方式:实例方法是一个普通的函数,类方法和静态方法都是通过函数装饰器的方式实现的;实例方法需要传入self参数,类方法需要传入cls参数,静态方法无需传入self参数或者是cls参数(但不等同于不能传入参数)。
  • 调用方式:实例方法只能被类的实例对象调用,类方法和静态方法可以被类或类的实例对象调用;实例方法只能通过self参数来调用其他实例属性或方法,类方法可以通过cls参数或者类名来调用其他类属性或方法,静态方法可以通过类名来调用其他静态方法。
  • 访问权限:实例方法可以访问类中的所有属性和方法以及实例中的属性和方法,而类方法和静态方法只能访问类中的属性和方法。
  • 应用场景:实例方法通常用来处理与具体实例相关的逻辑,比如初始化属性,修改状态,显示信息等;类方法通常用来处理与整个类相关的逻辑,比如创建实例,修改类属性,显示类信息等;静态方法通常用来处理与类无关的逻辑,比如工具函数,辅助函数等。

下面是一个简单的例子来说明这些区别:

# 定义一个Person类
class Person:
    # 定义一个公有类属性
    species = "Homo sapiens"

    # 定义一个私有类属性
    __population = 0

    # 定义一个公有初始化方法
    def __init__(self, name, age):
        # 定义两个公有实例属性
        self.name = name
        self.age = age
        # 定义一个私有实例属性
        self.__id = Person.__population
        # 调用一个私有类方法
        Person.__increase_population()

    # 定义一个公有实例方法
    def say_hello(self):
        print(f"Hello, I am {self.name}, and I belong to {self.species}.")
        # 调用一个私有实例方法
        self.__show_id()

    # 定义一个私有实例方法
    def __show_id(self):
        print(f"My ID is {self.__id}.")

    # 定义一个公有类方法
    @classmethod
    def say_species(cls):
        print(f"We are {cls.species}.")
        # 调用一个私有类方法
        cls.__show_population()

    # 定义一个私有类方法
    @classmethod
    def __show_population(cls):
        print(f"Our population is {cls.__population}.")

    # 定义一个私有类方法
    @classmethod
    def __increase_population(cls):
        cls.__population += 1

    # 定义一个公有静态方法
    @staticmethod
    def is_adult(age):
        return age >= 18

# 创建两个Person类的实例对象
p1 = Person("Alice", 20)
p2 = Person("Bob", 25)

# 调用两个公有实例方法
p1.say_hello() 
# 输出 Hello, I am Alice, and I belong to Homo sapiens.
# 输出 My ID is 0.
p2.say_hello() 
# 输出 Hello, I am Bob, and I belong to Homo sapiens.
# 输出 My ID is 1.

# 调用两个公有类方法
Person.say_species() 
# 输出 We are Homo sapiens.
# 输出 Our population is 2.

# 调用一个公有静态方法
print(Person.is_adult(16)) # 输出 False
print(Person.is_adult(21)) # 输出 True

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值