Python之面向对象
目录
类(Class)
对象(Object)
属性(Attribute)
方法(Method)
封装(Encapsulation)
继承(Inheritance)
多态(Polymorphism)
抽象类(abstract)
[鸭子类型(Duck Typing)](#鸭子类型(Duck Typing))
###### [类的特殊属性与魔法方法](#类的特殊属性与魔法方法) 1. [特殊属性](#特殊属性) 1. [`__init__`](#__init__) 2. [`__name__`](#__name__) 3. [`__doc__`](#__doc__) 4. [`__base__`](#__base__) 5. [`__bases__`](#__bases__) 6. [`__dict__`](#__dict__) 7. [`__module__`](#__module__) 8. [`__clasee__`](#__clasee__) 2. [装饰器](#装饰器) 1. [@classmethod](#@classmethod) 2. [@staticmethod](#@staticmethod) 3. [@property](#@property) 1. [@setter](#@setter) 2. [@deleter](#@deleter)
什么是面向对象编程?
面向对象编程(Object-Oriented Programming,OOP)是一种常用的编程思想,它将计算机程序组织为对象的集合,这些对象可以相互交互完成任务。
面向对象编程的核心思想就是将数据和操作数据的方法(函数)组合成一个称为对象的独立单元,每个对象都有自己的属性和方法,通过定义和实例化类,我们可以创建多个相似结构的对象以重复使用。面向对象的三大特性:封装,继承,多态。
类(Class)
类是一种用户自定义的数据类型,它由数据和方法组成。数据表示属性,方法表示行为,其中属性是类的成员变量,方法是一组操作数据的代码,具体格式如下:
class 类名:
类属性 = 值
python
class ATM:
def __init__(self):
self.__user = {}
atm = ATM()
print(type(atm))# <class '__main__.ATM'>
对象(Object)
对象就是类的实例化结果,它具有特定的状态和行为,如果说类是我们描述对象的模版和蓝图,那么对象就是继承了类的属性和定义的方法的实例,我们可以通过操作对象来实现各种功能:
python
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=自定义数据)
python
class Human:
text = 'Hello'
def __init__(self, name):
self.name = name
p = Human(name='张三')
print(p.name) # 张三
方法(Method)
方法(Method)是类中定义的函数成员,用于描述对象的行为和操作。方法提供了一种封装数据和操作数据的方式,使得相关的功能可以组织在一起。
- 在类的方法定义中,第一个参数通常被命名为
self
,它表示当前的对象实例。通过self
,我们可以访问和操作对象的属性和其他方法。
python
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
- 实例方法和静态方法:实例方法是与特定对象相关联的方法,可以访问对象的属性。静态方法是不依赖于特定对象的方法,它们与类本身相关联,并且无法访问对象的属性。
- 需要注意的是,在静态方法中无法直接调用实例属性,如果想要访问类中的数据,可以考虑将数据定义为类属性
python
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)
封装是指将数据和行为打包到一个类中,并可以控制外部访问的级别。封装可以保护数据和方法的访问,并且提高代码的可维护性。
具体隐藏操作可以在参数前面加上两个下划线(__
),参数和方法都是同理
python
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
函数以及其他类属性了,但我们依然可以在类中使用被封装的数据,如果想在外部调用,我们可以调用未被封装的方法:
python
class MyClass:
__name = '张三'
__age = 17
def __fun1(self):
print(self.__name)
def fun2(self):
self.__fun1()
p = MyClass()
p.fun2() # 张三
继承(Inheritance)
继承(Inheritance)是面向对象编程中的一个重要概念,它允许我们创建一个新类(称为子类或派生类),从现有的类(称为父类或基类)继承属性和方法。
以下是继承的一些重要特点:
- 继承父类的属性和方法:子类继承了父类的所有非私有属性和方法。这意味着子类可以直接使用父类的属性和方法,无需重新定义。
- 添加新属性和方法:子类可以在继承父类的基础上,添加自己的新属性和方法。这样可以扩展父类的功能,实现更具体的行为。
- 重写父类的方法:子类可以重新定义(重写)父类的方法,以适应自己的需求。这使得子类可以修改继承的方法的行为。
- 多层继承:一个子类可以同时继承多个父类 ,形成多层继承关系。这种继承关系被称为多继承。如果多继承时出现同名的方法和属性则会根据就近原则,先找到括号左边的类,如果左边的类找到了则不会再找右边的类
python
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)
对于一个父类中的函数,我们想让他在各个子类中都能实现,并且在实现过程中具有子类的独特含义,此时便需要引入多态的概念。
python
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
装饰器来标记抽象方法,具体如下:
python
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中如果你不是鸭子类型或是它的子类,即使你有同名的鸭子函数,你也不是鸭子类型。
话不多说直接上代码:
首先定义一个鸭子类型,真正的鸭子会'嘎嘎嘎'和走鸭步
python
class Duck:
def duck_cry(self):
print('嘎嘎嘎')
def duck_walk(self):
print('走鸭步')
接下来我们定义一个检查类,类中的check
方法会接受一个实例化对象,然后用该对象调用鸭子类型中的函数,如果程序没有报错,则证明它既会嘎嘎叫也会走鸭步(即 它就是一只鸭子)
python
class Check:
def check(self, duck):
duck.duck_cry()
duck.duck_walk()
print("检查完毕,是只鸭子")
这时我们将鸭子类型的实例化对象放入检查类中检查,结果是显而易见的
python
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)
"""
嘎嘎嘎
走鸭步
检查完毕,是只鸭子
"""
这时来了条狗,它既不会走鸭步也不会学鸭叫,那么程序就会报错
python
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'?
然而,重点来了,现在来了一只小鸡,它即会学鸭叫也会走鸭步,那么这个时候检查类不会管他到底是鸡还是鸭,只要你会这俩技能就能放行
python
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__
初始化
python
class Dog:
def __init__(self, name):
self.name = name
d = Dog(name='小黑')
print(d.name) # 小黑
__name__
类的名字(字符串)
python
print(__name__) # __main__
由此引申出判断当前模块是否为主函数
python
# module.py
if __name__ == '__main__':
print("这是主函数")
else:
print("这不是主函数")
如果在当前模块执行该代码则会打印
python
这是主函数
如果在其他文件调用module.py
则会打印
python
这不是主函数
__doc__
类的文档字符串,也就是查看"""
三引号的注释
python
def greet(name):
"""
这是注释
"""
print("Hello")
print(greet.__doc__)
# 这是注释
__base__
类的第一个父类
python
class Animal:
pass
class Mammal:
pass
class Dog(Animal, Mammal):
pass
print(Dog.__base__)
# <class '__main__.Animal'>
__bases__
类所有父类构成的元组
python
class Animal:
pass
class Mammal:
pass
class Dog(Animal, Mammal):
pass
print(Dog.__bases__)
# (<class '__main__.Animal'>,<class'__main__.Mammal'>)
__dict__
类的字典属性
python
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__
类定义所在的模块
python
class Dog:
name = "小黄"
def __init__(self, age):
self.age = age
d = Dog(age=19)
print(d.__module__) # __main__
print(Dog.__module__) # __main__
__class__
实例对应的类(仅新式类中)
python
class Dog:
name = "小黄"
def __init__(self, age):
self.age = age
d = Dog(age=19)
print(d.__module__) # <class '__main__.Dog'>
装饰器
@classmethod
将方法转换为类方法,用cls
访问类属性
python
class MyClass:
name = 'Tony'
@classmethod
def my_class_method(cls):
print(cls.name)
MyClass.my_class_method() # Tony
@staticmethod
将方法转换为静态方法,与类和对象的属性无关,也不能通过self
或cls
调用
python
class MyClass:
name = 'Tony'
@staticmethod
def my_static_method():
print("我是静态方法")
MyClass.my_static_method() # 我是静态方法
@property
将一个方法转换为属性,前提是有返回值,可直接通过.对象名
调用
python
class MyClass:
name = 'Tony'
@property
def get_name(self):
return self.name
m = MyClass()
print(m.get_name)
@setter
通过@property
将方法转换成属性后并不能直接对其进行修改值操作 否则会直接报错
python
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
包裹即可
python
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可以删除类属性
python
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'
并且可以进行其他操作,比如重新赋值
python
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