Python类与对象

目录

1. 语法

1.1. 定义类

1.2. 调用类

1.3. 方法

2. 封装

2.1. 属性

2.2. 类与方法的相互调用

2.3. 私有方法

3. 继承

3.1. 单继承

3.2. 多继承

3.3. 连续继承

3.4. 调用父类同名方法

3.5. 查看继承关系

4. 多态

5. 装饰器

5.1. 类方法 @classmethod

5.2. 静态方法 @staticmethod

5.3. 类方法与静态方法的区别

6. 特殊方法

if __name__ = '__main__' 阻止自动调用

__init__ 定义实例属性

__str__ 定义类的返回值

__del__ 回收内存

__len__ 返回对象的长度

__getattr__ 自定义属性调用


1. 语法

1.1. 定义类

class 类名(object):
    代码块
    ......
  • class:必须的关键字
  • 类名:自定义名称,遵循标准驼峰写法( 例如:Graphic report ---> GraphicReport)
  • 括号:python3表示继承某个类
  • object:超类,继承所有

python2的中经典类

class MyClass:
    代码块

python3的新式类

class MyClass(object):
    代码块

class MyClass():    #继承为空,默认为object
    代码块

1.2. 调用类

实例化对象调用

>>> class MyClass(object):
        def func1(self):
            print('这是类中定义的一个方法')

#将对象实例化后,调用该类中的方法
>>> m = MyClass()
>>> m.func1()
这是类中定义的一个方法

#也可以直接调用
>>> MyClass().func1()
这是类中定义的一个方法

1.3. 方法

实例方法

class MyClass(object):
    #定义一个实例方法
    def func(self):
        代码块
  • func:封装的一个实例方法(自定义方法名)
  • self:表示类的实例对象

类方法

class MyClass(object):
    #定义一个类方法
    @classmethod
    def func(cls):
        代码块

方法上一层加入了 @classmethod,表示定义一个类方法

  • func:封装的一个类方法(自定义方法名)
  • cls:表示类的本身

静态方法

class MyClass(object):
    #定义一个静态方法
    @staticmethod
    def func:
        代码块

方法上一层加入了 @staticmethod,表示定义一个静态方法。而静态方法不需要传入形参,并且独立,与类中其他方法无法互相调用。

  • func:封装的一个静态方法(自定义方法名)

2. 封装

2.1. 属性

共有类属性,私有类属性,实例属性

>>> class MyClass(object):
        a = 1   #共有类属性
        __b = 2 #私有类属性(以双下划线开头)
    
        def __init__(self):
            self.c = 3  #实例属性(必须以self开头)
        
        def func1(self):
            #调用属性必须以self开头,不论共有还是私有
            print(self.a)
            print(self.__b)
            print(self.c)

>>> m = MyClass()
>>> m.func1()
1
2
3

#外面只能调用共有类属性或实例属性,不能调用私有属性
>>> print(m.a)
1
>>> print(m.b)
AttributeError: 'MyClass' object has no attribute 'b'
>>> print(m.c)
3

类属性与实例属性名相同,默认使用实例属性

>>> class MyClass(object):
        a = 1

        def __init__(self):
            self.a = 2    #优先

        def func1(self):
            print(self.a)

>>> MyClass().func1()
2

调用自己方法中的变量,不使用self开头

>>> class MyClass(object):
        def __init__(self):
            self.a = 1

        def func1(self):
            a = 2
            print(self.a)    #调用init中的属性
            print(a)         #调用自己定义的属性

>>> MyClass().func1()
1
2

类外面增加属性

>>> class MyClass(object):
        def func1(self):
            print(self.b)

>>> m = MyClass()
>>> m.b = 2
>>> m.func1()
2

类外面修改属性

>>> class MyClass(object):
        def __init__(self):
            self.a = 1

        def func1(self):
            print(self.a)    #本身self.a为1,但是修改之后变成了2

>>> m = MyClass()
>>> m.a = 2
>>> m.func1()
2

类外面删除属性

>>> class MyClass(object):
        def __init__(self):
            self.a = 1

        def func1(self):
            print(self.a)

>>> m = MyClass()
>>> del m.a     #删除属性
>>> m.func1()
AttributeError: 'MyClass' object has no attribute 'a'

2.2. 类与方法的相互调用

类之间的相互调用

>>> class A(object):
        def func1(self):
            print('A类')

>>> class B(object):
        A().func1()    #调用其他类中的方法,需要实例化

>>> B()
A类

方法之间的相互调用

>>> class MyClass(object):
        def func1(self):
            print('第1个函数')

        def func2(self):
            self.func1()    #调用同一个类中的函数,以self开头

>>> MyClass().func2()
第1个函数

2.3. 私有方法

定义一个私有方法,以双下划线开头。私有方法只能在内部使用,外部无法调用

>>> class MyClass(object):
        def __func1(self):
            print('这是一个私有方法')

        def func2(self):
            print('这是一个普通方法')
            self.__func1()        #内部调用这个私有方法

>>> MyClass().func1()    #外部无法调用私有方法
AttributeError: 'MyClass' object has no attribute 'func1'. Did you mean: 'func2'?

>>> MyClass().func2()    #可以通过内部去调用
这是一个普通方法
这是一个私有方法

3. 继承

3.1. 单继承

继承父类的全部方法和属性

>>> class A(object):
        a = 1
        def func1(self):
            print('A类')

>>> class B(A):     #继承父类A中的全部方法和属性
        def func2(self):
            print(f'A中的变量 a = {self.a}')

>>> B().func1()
>>> B().func2()
A类
A中的变量 a = 1

子类重写

如果子类与父类的方法名、属性名相同,调用子类时默认使用子类的方法、属性

>>> class A(object):
        a = 1
        def func1(self):
            print('A类')

>>> class B(A):
        a = 2
        def func1(self):
            print(self.a)   #使用当前类的属性

>>> B().func1()    #调用子类的方法
2

3.2. 多继承

继承多个类属性名或方法名相同时,默认使用继承的第一个父类

>>> class A(object):
        a = 1

>>> class B(object):
        a = 2

>>> class C(A, B):    #先继承A,后继承B
        def func1(self):
            print(self.a)    #默认调用A的属性

>>> C().func1()
1

3.3. 连续继承

最小类可以继承上层全部关系的属性、方法

>>> class A(object):
        def func1(self):
            print('A类')

>>> class B(A):
        def func2(self):
            print('B类')

>>> class C(B):
        pass

>>> C().func1()
>>> C().func2()
A类
B类

3.4. 调用父类同名方法

当子类与父类同名,实例化对象调用时默认子类

>>> class A(object):
        def func(self):
            print('A类')

>>> class B(A):
        def func(self):
            print('B类')

>>> class C(B):
        def func(self):    #优先使用本身的方法
            print('C类')

>>> C().func()
C类

super 函数调用上一个父类,不需要指定类名。缺点就是只能调用自己的上一级

>>> class A(object):
        def func(self):
            print('A类')

>>> class B(A):
        def func(self):
            print('B类')

>>> class C(B):
        def func(self):
            super().func()    #调用上一级类方法,不需要指定类名

>>> C().func()
B类

指定类名调用同名方法

>>> class A(object):
        def func(self):
            print('A类')

>>> class B(A):
        def func(self):
            print('B类')

>>> class C(B):
        def func(self):
            B.func(self)    #指定B类(需要参数self)
            A.func(self)    #指定A类(需要参数self)

>>> C().func()
B类
A类

3.5. 查看继承关系

使用 __mro__ 查看继承关系

class A(object):
    def func1(self):
        print('A类')

class B(A):
    def func2(self):
        print('B类')

class C(B):
    pass

print(C.__mro__)    # 查看C类的继承关系

  • 从左往右表示:C继承B,B继承A,A继承object

4. 多态

多态就是利用继承的方法去实现各种灵活性的调用,举例:

>>> class A(object):
        """这是一个程序"""
        name = '[百度]'

>>> class B(A):
        """这是一个子进程,继承父类公共方法、属性"""
        def Func(self):
            print(f'{self.name}程序开启搜索引擎')

>>> class C(A):
        """这是一个子进程,继承父类公共方法、属性"""
        def Func(self):
            print(f'{self.name}程序返回结果')

>>> class CallingProgram(object):
        """调用程序的各个子进程"""
        def Call_Func(self, var):
            var.Func()    #去调用指定类中的方法,调用的方法同名

>>> b = B()
>>> c = C()
>>> cp = CallingProgram()

>>> cp.Call_Func(b)
>>> cp.Call_Func(c)
[百度]程序开启搜索引擎
[百度]程序返回结果

5. 装饰器

5.1. 类方法 @classmethod

  • 用 classmethod 来声明一个类方法,适用于在不需要实例化对象的情况下提供类级别的操作(比如:创建实例、返回类的状态、更改类的状态等)。
  • 类方法是与类绑定的一种方法,而不是与对象实例绑定的方法。
  • 类方法可以访问类的数据属性和其他类方法,但不能访问实例的数据属性和实例方法。

类中定义了一个实例方法和一个类方法,看一下两者之间的调用区别

>>> class MyClass(object):
        def func1(self):
            print('实例方法')

        @classmethod
        def func2(cls):
            print('类方法')

#调用实例方法,需要实例化
>>> m = MyClass()
>>> m.func1()
实例方法

#调用类方法,不需要实例化
>>> MyClass.func2()
类方法

为什么调用类方法不需要实例化?

  • 因为声明的类方法就是类的本身,形参 cls 就相当于类名,所以本身调用本身不需要实例化。实例化是用来调用实例方法的

类方法使用类属性时,以 cls 方式调用

>>> class MyClass(object):
        a = 1

        @classmethod
        def func(cls):
            print(f'公共类属性a为{cls.a}')

>>> MyClass.func()
公共类属性a为1

类方法无法调用实例属性,仅适用于类级别上的操作。

>>> class MyClass(object):
        def __init__(self):
            self.a = 1

        @classmethod
        def func(cls):
            print(f'实例属性a为{cls.a}')

>>> MyClass.func()
AttributeError: type object 'MyClass' has no attribute 'a'

记录实例使用的次数

>>> class MyClass(object):
        class_count = 0

        def __init__(self):
            MyClass.class_count += 1

        def func1(self):
            print('func1...')

        def func2(self):
            print('func2...')

        @classmethod
        def func3(cls):
            print(f'计数器的值:{cls.class_count}')
            return cls.class_count

>>> MyClass().func1()
>>> MyClass().func2()
>>> MyClass().func3()
>>> MyClass().func3()
func1...
func2...
计数器的值:3
计数器的值:4

需要注意的是,实例化对象后,使用该变量调用并不会增加计数器

>>> m = MyClass()    #实例化n个对象,计数器 +n
>>> m.func3()
>>> m.func3()
计数器的值:1
计数器的值:1

5.2. 静态方法 @staticmethod

  • 静态方法既不需要传递类对象,也不需要传递实例对象(没有形参)。静态方法可以独立于类的任何实例或类级别而存在,因此它们是最为模块化、灵活和可维护的方法类型之一。

静态方法的特点

  1. 静态方法不能访问类的变量,因为它不会接收任何与类相关的参数。也就是说,它不能依赖于类的状态或实例,并且它应该是没有副作用的。
  2. 与其他类型的方法相比,静态方法不需要特殊的参数,但仍然可以使用默认参数。
  3. 静态方法往往更容易维护和测试,因为它们不会改变且不依赖于类的状态。
  4. 由于静态方法可以独立于类而存在,它们也可以很方便地跨越模块、包和应用程序。

静态方法的调用(不需要实例化对象)

>>> class MyClass():
        @staticmethod
        def func(n):
            print(n)
>>> MyClass.func(1)
1

静态方法本身独立,属性也独立

无法调用实例属性
>>> class MyClass(object):
        def __init__(self):
            self.a = 1

        @staticmethod
        def func():
            print(f'实例属性a为{a}')

>>> MyClass.func()
NameError: name 'a' is not defined
无法调用实例方法
>>> class MyClass(object):
        def func1(self):
            print('实例方法')

        @staticmethod
        def func2():
            func1()     #调用func1
            MyClass().func1()    #即使通过类也无法调用

>>> MyClass.func()
AttributeError: type object 'MyClass' has no attribute 'func'. Did you mean: 'func1'?
实例方法也无法调用静态方法
>>> class MyClass(object):
        @staticmethod
        def func1():
            print('静态方法')

        def func2(self):
            self.func1()

>>> MyClass().fun2()
AttributeError: 'MyClass' object has no attribute 'fun2'. Did you mean: 'func2'?
静态方法无法继承
>>> class A(object):
        @staticmethod
        def func1():
            print('静态方法')

>>> class B(A):
        def func2(self):
            self.func1()    #调用父类中的静态方法

>>> B().fun2()
AttributeError: 'B' object has no attribute 'fun2'. Did you mean: 'func2'?

5.3. 类方法与静态方法的区别

参数

  • 类方法第一个参数默认 cls 代表类的本身,非实例本身。
  • 静态方法不需要额外的形参,可以把它当成一个独立函数。

访问类属性

  • 类方法可以访问类中的其他属性。
  • 静态方法不可以访问类中的其他属性。

继承

  • 类方法可以被继承和重写。
  • 静态方法无法被继承。

用途

  • 类方法通常用于操作与类有关的属性和方法,适用于通用代码。
  • 静态方法通常用于实现可维护性高的代码,适用于独立和复用的代码块。

6. 特殊方法

if __name__ = '__main__' 阻止自动调用

  • __name__是Python中的一个内置变量,表示模块名或程序名。
  • 当执行一个模块时,Python会自动将该模块的__name__设置为字符串"__main__"。因此,当我们在一个模块中使用if __name__ == "__main__":语句时,表示只有在该模块直接被执行时,该语句才会成立。

举个例子,分别在2个文件中写入以下代码

"""文件a"""
class MyClass(object):
    def func1(self):
        print('func1...')

MyClass().func1()
"""文件b"""
import a
print('func2...')

 文件b导入文件a模块,执行文件b

func1...
func2...
  • 文件a中的模块被自动调用了(输出func1...)

使用 if __name__ == '__main__': 方法

"""文件a"""
class MyClass(object):
    def func1(self):
        print('func1...')

if __name__ == '__main__':
    MyClass().func1()
"""文件b"""
import a
print('func2...')

文件b导入文件a模块,执行文件b

func2...
  • 文件a中的模块被无法被自动调用,仅输出func2...

__init__ 定义实例属性

>>> class MyClass(object):
        def __init__(self):
            self.a = 1

init 可以定义属性,类也可以定义属性,那么问题来了,init 存在的作用是啥呢?

可以发现公共类属性无法通过传参的方式动态定义属性

class MyClass(object):
    a = 1

MyClass()

但使用 init 就很好地解决了这一问题

>>> class MyClass(object):
        def __init__(self, a, b):
            self.a = a
            self.b = b

        def func1(self):
            print(f'a的值为{self.a}, b的值为{self.b}')

>>> m = MyClass(1, 2)
>>> m.func1()
a的值为1, b的值为2

        当然了,封装的方法也可以传参,不过传入的参数独立使用的属性,其他方法无法使用。而 init 传入的参数是公共方法,所以这个方法根据实际情况使用就好。

__str__ 定义类的返回值

这个方法的作用是什么呢?先来看看打印类的结果

>>> class MyClass(object):
        pass

>>> print(MyClass())
<__main__.MyClass object at 0x0000014E6FC82EC0>

直接打印类,返回了一个内存地址(其实这是默认的返回值)。如果我们不需要内存地址,而想让它返回这个类的作用,那么定义一个 __str__ 即可

>>> class MyClass(object):
        def __str__(self):
            return "这个类是一个示例"

>>> print(MyClass())
这个类是一个示例

__del__ 回收内存

  • __del__ 函数在删除实例时调用,回收内存(Python自动调用)

看一个简单例子

>>> class MyClass(object):
        def func1(self):
           print('执行程序A')

        def __del__(self):
            print('程序执行完毕,回收内存!')

>>> MyClass().func1()
执行程序A
程序执行完毕,回收内存!

封装的方法 func1 执行完了以后才会调用del函数,如果类外面还有其他程序又会怎么样呢?

>>> class MyClass(object):
        def func1(self):
           print('执行程序A')

        def __del__(self):
            print('程序执行完毕,回收内存!')

>>> MyClass().func1()
>>> print('执行程序B')
执行程序A
程序执行完毕,回收内存!
执行程序B
  • 可以看到 MyClass 执行完之后就立即回收了内存

如果将实例化对象赋值呢?

>>> class MyClass(object):
        def func1(self):
           print('执行程序A')

        def __del__(self):
            print('程序执行完毕,回收内存!')

>>> m = MyClass()
>>> m.func1()
>>> print('执行程序B')
执行程序A
执行程序B
程序执行完毕,回收内存!
  • 可以发现赋值后调用 MyClass,执行完func1之后并没有回收内存,而是直接去执行了下面的 "程序B",再回收内存。说明此时 MyClass 执行完并没有直接回收。

我们试试 del 呢

>>> class MyClass(object):
        def func1(self):
           print('执行程序A')

        def __del__(self):
            print('程序执行完毕,回收内存!')

>>> m = MyClass()
>>> m.func1()
>>> del m
>>> print('执行程序B')
执行程序A
程序执行完毕,回收内存!
执行程序B
  • 手动删除了MyClass 后自动回收该类的内存。

Python采用自动引用计数实现垃圾回收机制。

  • 每一个Python对象配置了一个计数器,初始值为0,当变量引用该实例对象,计数器 +1;当一个变量取消对该实例对象的引用,则计数器 -1。
  • 如果Python对象的计数器值为0,则表明没有变量引用该Python对象,即程序不再需要它,此时Python将自动调用__del__()方法将其回收。

需要注意的是:

        del [对象] 并不会主动调用 __del__ 方法,只有引用计数为 0 时,__del__()才会被执行,并且定义了__del_() 的实例无法被 Python 循环垃圾收集器收集,所以尽量不要自定义__del__()。一般情况下,__del__() 不会破坏垃圾处理器。

__len__ 返回对象的长度

通常用于计算容器类型对象的元素数量。

>>> class MyClass(object):
        def __init__(self, num):
            self.num = num

        def __len__(self):
            return len(self.num)

>>> mc = MyClass([1, 2, 3])
>>> print(len(mc))
3


__getattr__ 自定义属性调用

当调用一个不存在的属性时,会调用该方法。可以自定义该方法以实现动态属性调用。

>>> class MyClass:
        def __init__(self, name):
            self.name = name

        def __getattr__(self, attr):
            if attr == "age":
                return 18
            else:
                raise AttributeError(f"类 '{self.__class__.__name__}' 中没有属性 '{attr}'")

>>> mc = MyClass("小李")
>>> print(mc.name)
>>> print(mc.age)
>>> print(mc.gender)
小李
18
AttributeError: 类 'MyClass' 中没有属性 'gender'

你可能感兴趣的:(Python,开发语言,python)