Python之面向对象

发布时间:2024年01月04日

Python之面向对象

目录

  1. 类(Class)
  2. 对象(Object)
  3. 属性(Attribute)
  4. 方法(Method)
  5. 封装(Encapsulation)
  6. 继承(Inheritance)
  7. 多态(Polymorphism)
  8. 抽象类(abstract)
  9. 鸭子类型(Duck Typing)
  10. 类的特殊属性与魔法方法
    1. 特殊属性
      1. __init__
      2. __name__
      3. __doc__
      4. __base__
      5. __bases__
      6. __dict__
      7. __module__
      8. __clasee__
    2. 装饰器
      1. @classmethod
      2. @staticmethod
      3. @property
        1. @setter
        2. @deleter

什么是面向对象编程?

面向对象编程(Object-Oriented Programming,OOP)是一种常用的编程思想,它将计算机程序组织为对象的集合,这些对象可以相互交互完成任务。

面向对象编程的核心思想就是将数据和操作数据的方法(函数)组合成一个称为对象的独立单元,每个对象都有自己的属性和方法,通过定义和实例化类,我们可以创建多个相似结构的对象以重复使用。面向对象的三大特性:封装,继承,多态。

类(Class)

类是一种用户自定义的数据类型,它由数据和方法组成。数据表示属性,方法表示行为,其中属性是类的成员变量,方法是一组操作数据的代码,具体格式如下:

class 类名:
类属性 = 值

class ATM:
    def __init__(self):
        self.__user = {}
atm = ATM()
print(type(atm))# <class '__main__.ATM'>

对象(Object)

对象就是类的实例化结果,它具有特定的状态和行为,如果说类是我们描述对象的模版和蓝图,那么对象就是继承了类的属性和定义的方法的实例,我们可以通过操作对象来实现各种功能:

class Human:
    text = 'Hello'

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

    def say_hello(self):
        print(self.text, end='')
        print(self.name)


p = Human(name='张三')
print(p.text) # Hello
p.say_hello() # Hello张三

我们可以通过p.text直接调用Human类中的text数据,并且同时也能够使用Human类中的方法say_hello()

属性(Attribute)

我们可以用__init__方法初始化属性后,再由实例化将其赋予新的数据,具体格式如下:

class P:
类属性 = 值
def init(self, 参数1):
self.属性1 = 参数1

p = P(参数1=自定义数据)

class Human:
    text = 'Hello'

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


p = Human(name='张三')
print(p.name) # 张三

方法(Method)

方法(Method)是类中定义的函数成员,用于描述对象的行为和操作。方法提供了一种封装数据和操作数据的方式,使得相关的功能可以组织在一起。

  • 在类的方法定义中,第一个参数通常被命名为 self,它表示当前的对象实例。通过 self,我们可以访问和操作对象的属性和其他方法。
class Person:
    def __init__(self, name):
        self.name = name

    def say_hello(self):
        print(f"Hello, my name is {self.name}")

# 创建对象
person = Person("Alice")

# 调用对象的方法
person.say_hello()  # 输出:Hello, my name is Alice
  • 实例方法和静态方法:实例方法是与特定对象相关联的方法,可以访问对象的属性。静态方法是不依赖于特定对象的方法,它们与类本身相关联,并且无法访问对象的属性。
    • 需要注意的是,在静态方法中无法直接调用实例属性,如果想要访问类中的数据,可以考虑将数据定义为类属性
class MyClass:
    def instance_method(self):
        # 实例方法,通过 self 访问对象的属性
        print("This is an instance method.")

    @staticmethod
    def static_method():
        # 静态方法,无需访问对象的属性
        print("This is a static method.")

# 创建对象
obj = MyClass()

# 调用实例方法
obj.instance_method()  # 输出:This is an instance method.

# 调用静态方法
MyClass.static_method()  # 输出:This is a static method.

封装(Encapsulation)

封装是指将数据和行为打包到一个类中,并可以控制外部访问的级别。封装可以保护数据和方法的访问,并且提高代码的可维护性。

具体隐藏操作可以在参数前面加上两个下划线(__),参数和方法都是同理

class MyClass:
    __name = '张三'
    __age = 17

    def __fun1(self):
        print(self.__name)


p = MyClass()
p.fun1() # AttributeError: 'MyClass' object has no attribute 'fun1'

print(p.age) # AttributeError: type object 'MyClass' has no attribute 'age'

现在外部已经无法正常调用fun1函数以及其他类属性了,但我们依然可以在类中使用被封装的数据,如果想在外部调用,我们可以调用未被封装的方法:

class MyClass:
    __name = '张三'
    __age = 17

    def __fun1(self):
        print(self.__name)

    def fun2(self):
        self.__fun1()


p = MyClass()
p.fun2() # 张三

继承(Inheritance)

继承(Inheritance)是面向对象编程中的一个重要概念,它允许我们创建一个新类(称为子类或派生类),从现有的类(称为父类或基类)继承属性和方法。

以下是继承的一些重要特点:

  1. 继承父类的属性和方法:子类继承了父类的所有非私有属性和方法。这意味着子类可以直接使用父类的属性和方法,无需重新定义。
  2. 添加新属性和方法:子类可以在继承父类的基础上,添加自己的新属性和方法。这样可以扩展父类的功能,实现更具体的行为。
  3. 重写父类的方法:子类可以重新定义(重写)父类的方法,以适应自己的需求。这使得子类可以修改继承的方法的行为。
  4. 多层继承:一个子类可以同时继承多个父类,形成多层继承关系。这种继承关系被称为多继承。如果多继承时出现同名的方法和属性则会根据就近原则,先找到括号左边的类,如果左边的类找到了则不会再找右边的类
class Animal:
    def __init__(self, name):
        self.name = name

    def say(self):
        print("这是Animal")
        
        
class Dog(Animal):
    def __init__(self, name):
        super().__init__(name)

    def say_dog(self):
        print(f'{self.name},汪汪')


class Cat(Animal):
    def __init__(self, name):
        super().__init__(name)

    def say_cat(self):
        print(f'{self.name},喵喵')


d = Dog(name="这是狗")
c = Cat(name="这是猫")
d.say()  # 这是Animal
c.say()  # 这是Animal
d.say_dog()  # 这是狗,汪汪
c.say_cat()  # 这是猫,喵喵

多态(Polymorphism)

对于一个父类中的函数,我们想让他在各个子类中都能实现,并且在实现过程中具有子类的独特含义,此时便需要引入多态的概念。

class Animal:
    def __init__(self, name):
        self.name = name

    def cry(self):
        print("动物的叫声")


class Dog(Animal):
    def cry(self):
        print(f'{self.name},汪汪')


class Cat(Animal):
    def cry(self):
        print(f'{self.name},喵喵')


d = Dog(name="这是狗")
c = Cat(name="这是猫")
d.cry()  # 这是狗,汪汪
c.cry()  # 这是猫,喵喵

抽象类(abstract)

抽象类是一个不能被实例化的类,它仅用作其他类的基类或父类,如果强行实例化会报错。

抽象类的主要目的是为了提供一个通用的接口或协议,规范其子类应该实现的方法,子类必须实现抽象类中的所有抽象方法否则也会报错。

python中我们必须使用abc模块来创建抽象类,抽象类则要继承ABC类,并使用@abstractmethod装饰器来标记抽象方法,具体如下:

from abc import ABC, abstractmethod


class Parent(ABC):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def run(self):
        print('run')

    def walk(self):
        print('walk')

    @abstractmethod
    def eat(self):
        pass

class Son(Parent):
    def run(self):
        print('no run')

    def walk(self):
        print('no walk')

    def eat(self): # 如果这里不重写run方法也回报错
        print("eat")


s = Son(name='ni', age=11)
# p = Parent(name='a',age=10) :TypeError: Can't instantiate abstract class Son with abstract method eat
s.eat() # eat

鸭子类型(Duck Typing)

鸭子类型是python中的一种编程思想,他并不是指代具体的代码,而它的概念也来源于一句诗句:

 “当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
 							—— 詹姆斯·惠特科姆·莱利

这种概念在编程中也是如此,如果你有鸭子的函数,那你就是鸭子类。

这与java中的概念大相径庭,在java中如果你不是鸭子类型或是它的子类,即使你有同名的鸭子函数,你也不是鸭子类型。

话不多说直接上代码:

首先定义一个鸭子类型,真正的鸭子会’嘎嘎嘎’和走鸭步

class Duck:
    def duck_cry(self):
        print('嘎嘎嘎')

    def duck_walk(self):
        print('走鸭步')

接下来我们定义一个检查类,类中的check方法会接受一个实例化对象,然后用该对象调用鸭子类型中的函数,如果程序没有报错,则证明它既会嘎嘎叫也会走鸭步(即 它就是一只鸭子)

class Check:
    def check(self, duck):
        duck.duck_cry()
        duck.duck_walk()
        print("检查完毕,是只鸭子")

这时我们将鸭子类型的实例化对象放入检查类中检查,结果是显而易见的

class Duck:
    def duck_cry(self):
        print('嘎嘎嘎')

    def duck_walk(self):
        print('走鸭步')


class Check:
    def check(self, duck):
        duck.duck_cry()
        duck.duck_walk()
        print("检查完毕,是只鸭子")

        
duck = Duck()
p1 = Check()
p1.check(duck)
"""
嘎嘎嘎
走鸭步
检查完毕,是只鸭子
"""

这时来了条狗,它既不会走鸭步也不会学鸭叫,那么程序就会报错

class Dog:
    def dog_cry(self):
        print("我是狗,我只会汪汪叫")

    def dog_walk(self):
        print('走狗步')


class Check:
    def check(self, duck):
        duck.duck_cry()
        duck.duck_walk()
        print("检查完毕,是只鸭子")


dog = Dog()
p1 = Check()
p1.check(dog) # AttributeError: 'Dog' object has no attribute 'duck_cry'. Did you mean: 'dog_cry'?

然而,重点来了,现在来了一只小鸡,它即会学鸭叫也会走鸭步,那么这个时候检查类不会管他到底是鸡还是鸭,只要你会这俩技能就能放行

class Chicken:
    def __init__(self):
        print("其实我是鸡")

    def duck_cry(self):
        print('嘎嘎嘎')

    def duck_walk(self):
        print('走鸭步')


class Check:
    def check(self, duck):
        duck.duck_cry()
        duck.duck_walk()
        print("检查完毕,是只鸭子")


chicken = Chicken()
p1 = Check()
p1.check(chicken)
"""
其实我是鸡
嘎嘎嘎
走鸭步
检查完毕,是只鸭子
"""

这就证明了在python中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定的。

鸭子类型作为程序设计中的一种类型推断风格,适用于大部分脚本语言 / 动态语言 (如 Python、Ruby、Perl、Julia、JavaScript 等) 和 某些静态语言 (如 Golang,通常静态类型语言在编译前便已显式指定变量类型,而 Golang 却则在编译时推断变量类型)。

支持鸭子类型的语言,其解释器/编译器将会在解释/解析 (Parse) 或编译时推断对象类型。

类的特殊属性与装饰器

特殊属性

__init__

初始化

class Dog:
    def __init__(self, name):
        self.name = name


d = Dog(name='小黑')
print(d.name) # 小黑
__name__

类的名字(字符串)

print(__name__) # __main__

由此引申出判断当前模块是否为主函数

# module.py

if __name__ == '__main__':
    print("这是主函数")
else:
    print("这不是主函数")

如果在当前模块执行该代码则会打印

这是主函数

如果在其他文件调用module.py则会打印

这不是主函数
__doc__

类的文档字符串,也就是查看"""三引号的注释

def greet(name):
    """
    这是注释
    """
    print("Hello")

print(greet.__doc__)
# 这是注释
__base__

类的第一个父类

class Animal:
    pass


class Mammal:
    pass


class Dog(Animal, Mammal):
    pass


print(Dog.__base__)
# <class '__main__.Animal'>
__bases__

类所有父类构成的元组

class Animal:
    pass


class Mammal:
    pass


class Dog(Animal, Mammal):
    pass


print(Dog.__bases__)
# (<class '__main__.Animal'>,<class'__main__.Mammal'>)
__dict__

类的字典属性

class Dog:
    name = "小黄"

    def __init__(self, age):
        self.age = age


d = Dog(age=19)

print(d.__dict__)
# {'age': 19}

print(Dog.__dict__)
# {'__module__': '__main__', 'name': '小黄', '__init__': <function Dog.__init__ at 0x000002756A31DA80>, '__dict__': <attribute '__dict__' of 'Dog' objects>, '__weakref__': <attribute '__weakref__' of 'Dog' objects>, '__doc__': None}

__module__

类定义所在的模块

class Dog:
    name = "小黄"

    def __init__(self, age):
        self.age = age


d = Dog(age=19)
print(d.__module__) # __main__
print(Dog.__module__) # __main__
__class__

实例对应的类(仅新式类中)

class Dog:
    name = "小黄"

    def __init__(self, age):
        self.age = age


d = Dog(age=19)
print(d.__module__) # <class '__main__.Dog'>

装饰器

@classmethod

将方法转换为类方法,用cls访问类属性

class MyClass:
    name = 'Tony'

    @classmethod
    def my_class_method(cls):
        print(cls.name)


MyClass.my_class_method()  # Tony
@staticmethod

将方法转换为静态方法,与类和对象的属性无关,也不能通过selfcls调用

class MyClass:
    name = 'Tony'

    @staticmethod
    def my_static_method():
        print("我是静态方法")


MyClass.my_static_method()  # 我是静态方法
@property

将一个方法转换为属性,前提是有返回值,可直接通过.对象名调用

class MyClass:
    name = 'Tony'

    @property
    def get_name(self):
        return self.name


m = MyClass()
print(m.get_name)
@setter

通过@property将方法转换成属性后并不能直接对其进行修改值操作 否则会直接报错

class MyClass:
    name = 'Tony'

    @property
    def get_name(self):
        return self.name


m = MyClass()
m.get_name = 'Tom'
# AttributeError: property 'get_name' of 'MyClass' object has no setter

解决办法很简单,只要用@setter包裹即可

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

    @property
    def get_name(self):
        return self.name

    @get_name.setter
    def get_name(self, value):
        print(self.name) # Tony 由此可见self调用的是get_name中的name
        self.name = value


m = MyClass(name="Tony")
print(m.get_name) # Tony
m.get_name = 'Tom'
print(m.get_name) # Tom
@deleter

deleter可以删除类属性

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

    @property
    def get_name(self):
        return self.name

    @get_name.setter
    def get_name(self, value):
        print(self.name)
        self.name = value

    @get_name.deleter
    def get_name(self):
        del self.name
        print('get_name原属性已被删除')


m = MyClass(name="Tony")
print(m.get_name) # Tony
m.get_name = 'Tom'
print(m.get_name) # Tom
del m.get_name
print(m.get_name) # 报错:AttributeError: 'MyClass' object has no attribute 'name'

并且可以进行其他操作,比如重新赋值

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

    @property
    def get_name(self):
        return self.name

    @get_name.setter
    def get_name(self, value):
        print(self.name)
        self.name = value

    @get_name.deleter
    def get_name(self):
        self.name = 'Leven'
        print('name原属性已被删除,并赋予新的值', end='')



m = MyClass(name="Tony")
print(m.get_name) # Tony
m.get_name = 'Tom'
print(m.get_name) # Tom
del m.get_name
print(m.get_name) # name原属性已被删除,并赋予新的值Leven
文章来源:https://blog.csdn.net/AZURE060606/article/details/135395451
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。