Python学习第十三天--面向对象

一、面向过程和面向对象区别

面向过程:需要实现一个功能时,着重的是开发的步骤和过程,每个步都需要自己亲力亲为,需要编写代码(自己来做)

面向对象:需要实现一个功能时,不注重的是开发的步骤和过程,关心的是谁来帮我做这件事。

二、类与对象

类是一个抽象的概念,对象是一个实实在在的概念

1、类

类名习惯使用大驼峰命名法:首字母大写,私有类可用一个下划线开头

1)类的定义

class Test:
    pass #空类中使用pass
class Test:
    food = "chicken"
    def info(self):  #self指的是创建出来的对象
        print(self)  #打印当前对象的地址

2)类中包含内容

属性:实际上为类中定义的变量,该变量为属性

方法 :类中定义的函数

3)查看类的属性

Test.__dict__

>> {'__module__': '__main__', 'food': 'chicken', 'info': <function Test.info at 0x0000018F068BD1F0>, '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None}

4)查看单个属性的内容

Test.__dict__["food"]

>> chicken

5)增删改查类中的单个属性

改:Test.food = "beef"

删:del Test.food

增:Test.drink = "tea"

2、对象

1)创建对象

对象名=类名()

a = Test()
a.info()  #此处self为a

2)增删改查对象中的单个属性

改:a.food = "beef"

删:del a.food

增:a.drink = "tea"

3)在方法内通过self获取对象属性 

class Test():
    food = "chicken"
    def info(self):  #self指的是创建出来的对象
        print(self.food)

a = Test()
a.info()

>> chicken

 3、实例属性和类属性的区别

class B:
    num = 0  # 类属性

    def __init__(self, name):
        self.name = name  # 实例属性

    def test(self):
        print(self.name)  # 访问实例属性
        print(B.num)  # 通过类名访问类属性

 如果想使用类属性,需要使用:类名.类属性

三、构造函数

def __init__():

__init__()具有初始化作用,当该类被实例化时会自动执行该函数。那么通常可把要初始化的属性放在这个方法里去

无参构造:def __init__(self):

有参构造:def __init__(self,参数1,参数2...):

构造函数无返回值,一个类中只有一个构造函数

四、析构函数

def __del__(self):

析构函数用于在对象被清除后清除他所占用的内存空间,主要用于对那些长期占用内存的临时变量进行销毁

析构函数没有参数,无返回值,一个类中只有一个析构函数,在跳出对象作用域时会自动调用析构函数。可以在析构函数中加入输出语句,在看到输出语句内容时就表示该对象已被销毁

手动执行析构函数:del 对象名

五、__str__()方法

1、作用

能够在输出对象变量时打印自定义内容。

没有该方法时,输出的是对象的地址;使用该方法时,输出为该方法返回的自定义内容

2、注意

  1. __str__()必须返回一个字符串;
  2. 定义了 __str__()方法,在打印对象时,默认输出该方法的返回值

3、例子

class B:
    def __str__(self):
        return "this is my new name"

b = B()
print(b)

>> this is my new name

六、私有概念

1、定义私有

在python中定义私有变量只需要在变量名或函数名前加上"__"两个下划线,那么这个函数或变量就是私有的了。

2、注意事项

  • 类的私有属性和私有方法,都不能通过对象直接访问,但是可以在本类内部访问;
  • 类的私有属性和私有方法,不能被子类继承,子类也无法访问;
  • 类的私有属性和私有方法,往往用来处理类内部的事情,不通过对象来处理,起到安全作用

3、几种"_"形式

_x:告诉看代码的人这是我定义的私有属性或方法,但在计算机看来就是一个普通属性或方法
__x:真正的私有属性或方法
__x__:用户名字空间的魔法对象或属性

x__:用于避免与Python关键词的冲突

4、访问私有属性

1)不正规方法

对象名._类名__私有属性名 

2)正规方法

在类中创建使用私有属性的方法,让对象调用该方法即可

七、继承

继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码。

1、写法

class 类名(父类名):
    pass

2、Python中类的三种定义

1)class A(object):  这种定义任何Python版本均适用,object是所有类的基类

2)class A():

3)class A:

3、继承重写

当父类的方法不能满足子类需求时,可以对方法进行重写。

1)覆盖写:在子类中定义一个和父类同名的方法来实现

2)扩展写:

法一:父类名.方法

class Animal:
    def eat(self):
        print("吃")

class Dog(Animal):
    def eat(self):
        Animal.eat(self)
        print("啃骨头")

法二:super().方法

class Animal:
    def eat(self):
        print("吃")

class Dog(Animal):
    def eat(self):
        super().eat()
        print("啃骨头")

4、多继承

1)有多个父类的属性和方法,如果多个父类具有同名方法时候,就近调用(使用A.__mro__方法查看,产生的MRO列表就是一个简单的所有基类的线性顺序列表)

写法:class A(B,C,D):

2)子类从多个父类派生,如果子类有自己的构造函数,则父类的构造函数不会被使用;反之如果子类又没有自己的构造函数,则按顺序继承,哪个父类在最前面且它又有自己的构造函数,就继承它的构造函数;

3)在多继承中如果多个父类有同名方法时,法一可以指定重写某个方法,法二按照MRO列表内容顺序重写对应方法

(以下代码源自Python多继承与super使用详解_python super 多继承-优快云博客

class Parent(object):
    def __init__(self, name, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数
        print('parent的init开始被调用')
        self.name = name
        print('parent的init结束被调用')


class Son1(Parent):
    def __init__(self, name, age, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数
        print('Son1的init开始被调用')
        self.age = age
        super().__init__(name, *args, **kwargs)  # 为避免多继承报错,使用不定长参数,接受参数
        print('Son1的init结束被调用')


class Son2(Parent):
    def __init__(self, name, gender, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数
        print('Son2的init开始被调用')
        self.gender = gender
        super().__init__(name, *args, **kwargs)  # 为避免多继承报错,使用不定长参数,接受参数
        print('Son2的init结束被调用')


class Grandson(Son1, Son2):
    def __init__(self, name, age, gender):
        print('Grandson的init开始被调用')
        # 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍
        # 而super只用一句话,执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因
        # super(Grandson, self).__init__(name, age, gender) 效果和下面的一样
        super().__init__(name, age, gender)
        print('Grandson的init结束被调用')


print(Grandson.__mro__)  # 搜索顺序

gs = Grandson('grandson', 12, '男')

"""
(<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)
Grandson的init开始被调用
Son1的init开始被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Son1的init结束被调用
"""
Grandson的init结束被调用
  1.  super().__init__与类名.__init__,在单继承上用法基本无差
  2. 但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次
  3. 多继承时,使用super方法,对父类的传参数,必须把参数全部传递,否则会报错
  4. 单继承时,使用super方法,则不能全部传递,只能传父类方法所需的参数,否则会报错
  5. 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍, 而使用super方法,只需写一句话便执行了全部父类的方法

八、多态 

1、定义

子类重写父类方法,调用不同子类对象的相同父类方法,可以产生不同的执行结果。 

2、实现多态的两个前提:

  1. 继承:多态必须发生在父类与子类之间;
  2. 重写:子类重写父类方法 

九、类方法

1、理解

类方法是定义在类中的方法,通过装饰器​​@classmethod​​来标识。它的第一个参数是​​cls​​(表示类本身),而不是实例对象。类方法可以访问类的属性,并且可以在没有实例的情况下被调用。类方法常用于需要访问类属性或需要在类级别进行操作的场景。

2、定义格式

class 类名:
    @classmethod
    def 方法名(cls,形参):
        方法体

#调用格式
类名.方法名(实参)

3、使用类方法

类方法可以通过类名或实例对象来调用。当类方法被调用时,Python会自动传递类本身作为第一个参数 ​​cls​​。

class MyClass:
    @classmethod
    def my_classmethod(cls):
        print("Called on class:", cls)

# 通过类名调用
MyClass.my_classmethod()

# 通过实例调用
instance = MyClass()
instance.my_classmethod()

>> Called on class: <class '__main__.MyClass'>

4、使用场景

1)工厂方法:通过定义多个类方法来实现多个构造函数,每个类方法都可以有不同的参数和逻辑,以适应不同的创建对象的场景 

class Car:
    def __init__(self, make, model):
        self.make = make
        self.model = model

    @classmethod
    def from_make_model(cls, make, model):
        return cls(make, model)

    @classmethod
    def from_year(cls, year):
        # 假设我们有一个固定的品牌和模型对应于特定的年份
        make = "Toyota"
        model = f"Corolla {year}"
        return cls(make, model)

# 使用不同的工厂方法创建Car实例
car1 = Car.from_make_model("Honda", "Civic")
car2 = Car.from_year(2020)

print(car1.make, car1.model)  # 输出: Honda Civic
print(car2.make, car2.model)  # 输出: Toyota Corolla 2020

 2)在无需实例化的情况下使用类方法访问和修改类级别的属性

class MyClass:
    # 类级别的属性
    class_attribute = 0

    @classmethod
    def increment_class_attribute(cls):
        # 访问和修改类级别的属性
        cls.class_attribute += 1
        print(f"Class attribute incremented: {cls.class_attribute}")

    @classmethod
    def get_class_attribute(cls):
        # 访问类级别的属性
        return cls.class_attribute

# 调用类方法来操作类级别的属性
MyClass.increment_class_attribute()  # 输出: Class attribute incremented: 1
MyClass.increment_class_attribute()  # 输出: Class attribute incremented: 2

# 获取类级别的属性值
print(MyClass.get_class_attribute())  # 输出: 2

十、静态方法

1、理解

需要使用装饰器@staticmethod来进行修饰,静态方法既不需要传递类对象也不需要传递实例对象(形参没有self/cls),静态方法也能够通过实例对象和类对象去访问。

2、静态方法和类属性

由于静态方法不会自动接收类(cls)或实例(self)作为第一个参数,故它们不能直接访问类的属性或方法,除非这些属性或方法被显式传递给静态方法,或通过类名来访问类级别的属性。

class MyClass:
    class_attribute = "这是一个类级别的属性"

    @staticmethod
    def my_static_method():
        # 静态方法不能直接访问类级别的属性
        # 下面的代码会引发错误,因为静态方法没有cls参数
        # print(cls.class_attribute)
        
        # 但是,静态方法可以通过类名来访问类级别的属性
        print(MyClass.class_attribute)

    @classmethod
    def my_classmethod(cls):
        # 类方法可以直接访问类级别的属性
        print(cls.class_attribute)

# 调用静态方法和类方法
MyClass.my_static_method()  # 输出: 这是一个类级别的属性
MyClass.my_classmethod()  # 输出: 这是一个类级别的属性

3、使用场景

  • 工具函数:当方法不需要访问对象的任何属性时,它们可以被定义为静态方法。
  • 辅助函数:在类中定义一些辅助函数,这些函数与类的功能相关,但不需要访问类或实例的状态。
  • 数学计算:数学计算通常不需要访问对象的状态,因此它们可以被定义为静态方法。

十一、类方法,实例方法和静态方法综合应用

class Animal:
    # 类属性
    species = "Animalia"

    def __init__(self, name):
        # 实例属性
        self.name = name

    # 实例方法
    def speak(self):
        # 直接使用实例属性
        print(f"{self.name} makes a noise.")

    # 类方法
    @classmethod
    def get_species(cls):
        # 使用类属性
        print(f"The species of {cls.__name__} is {cls.species}.")

    # 静态方法
    @staticmethod
    def make_sound():
        # 静态方法不使用类或实例属性
        print("Some animal makes a sound.")

# 创建Animal类的实例
dog = Animal("Dog")

# 实例方法调用
dog.speak()  # 使用对象调用,输出: Dog makes a noise.
Animal.speak(dog)  # 使用类调用,需要传参数对象,输出: Dog makes a noise.

# 类方法调用
dog.get_species()  # 使用对象调用,输出: The species of Animal is Animalia.
Animal.get_species()  # 使用类调用,输出: The species of Animal is Animalia.

# 静态方法调用
dog.make_sound()  # 使用对象调用,输出: Some animal makes a sound.
Animal.make_sound()  # 使用类调用,输出: Some animal makes a sound.

十二、__new__()方法

1、作用

1)在内存中为对象分配空间

2)返回对象的引用,返回值会引发__init__()调用。

如果不设置,系统选择默认__new__()方法

2、深入理解

没有返回值的__new__()方法不会调用__init__()方法

class MyClass(object):
    def __init__(self):
        print("This is init")

    def __new__(cls, *args, **kwargs):
        print("This is new")

# 创建MyClass的实例
obj = MyClass()

print("This is object")

"""
输出:
This is new
This is object
"""

想调用__init__()方法,只需在最后加上返回值

class MyClass(object):
    def __init__(self):
        print("This is init")

    def __new__(cls, *args, **kwargs):
        print("This is new")
        #法一:
        return super().__new__(cls)
        #法二:
        return object.__new__(cls)
        #两种本质均是返回了对象的引用
    

# 创建MyClass的实例
obj = MyClass()

print("This is object")
"""
输出:
This is new
This is init
This is object
"""

 3、__new__()和__init__()方法区别

  • __new__ 方法是真正在创建实例对象时被第一个调用的方法,它负责创建并返回对象的引用。形参为cls
  • 如果 __new__ 方法成功返回了一个对象的引用(即有return),那么 __init__ 方法随后会被调用,以对这个对象进行初始化。形参为self

十三、单例模式

1、理解

让所有类在实例化时,指向同一个内存地址称之为单例模式

特点:每一次执行类名()返回的对象,内存地址是相同的 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值