【python】面向对象(类型定义&魔法方法)

目录


一、引言

        Python是一种面向对象的语言,它支持类(class)和对象(object)的概念。面向对象编程是一种程序设计模型,它将程序看作是由对象组成的,这些对象之间通过消息传递来相互通信和协作。

        Python中,类是创建对象的蓝图或模板,它定义了对象的基本结构和行为。对象是类的实例,可以根据类的定义来创建多个实例。每个对象都拥有类中定义的所有属性和方法。

下面是一个简单的Python类和对象的示例:

class Person:  
    def __init__(self, name, age):  
        self.name = name  
        self.age = age  
      
    def say_hello(self):  
        print(f"Hello, my name is {self.name} and I'm {self.age} years old.")  
  
# 创建Person类的实例  
p1 = Person("zs", 25)  
p2 = Person("ls", 30)  
  
# 调用对象的say_hello方法  
p1.say_hello()  # 输出:Hello, my name is Alice and I'm 25 years old.  
p2.say_hello()  # 输出:Hello, my name is Bob and I'm 30 years old.

 

在例子中,我们定义了一个名为Person的类,它有两个属性nameage,以及一个方法say_hello。通过调用Person类的构造函数__init__,我们创建了两个Person对象p1p2,分别代表”zs”和”ls”两个人。然后我们可以通过调用对象的say_hello方法来输出问候语。

面向对象编程的核心概念包括封装、继承、多态和抽象。在Python中,这些概念都有相应的语法和机制来支持。通过使用面向对象编程,我们可以更好地组织和管理代码,使其更易于维护和扩展

二、类型定义

1、什么是类型的定义?

        在编程中,类型是用于描述变量或数据结构的特征和行为的抽象概念。类型定义了变量或数据结构可以存储的数据的类型以及可以执行的操作。通过使用类型,程序员可以确保变量和数据结构中存储的数据是有效的,并且可以在编译时检测到潜在的错误。

        在许多编程语言中,类型可以分为静态类型和动态类型。静态类型在编译时进行类型检查,而动态类型则在运行时进行类型检查。

        静态类型定义了变量或数据结构在编译时必须具有的类型。这种类型通常在声明变量或数据结构时指定,并且不允许在运行时更改。静态类型的优点是编译器可以在编译时捕获许多类型相关的错误,从而提高代码的可靠性和安全性。然而,静态类型也限制了程序员在运行时动态更改变量类型的灵活性。

        动态类型定义了变量或数据结构在运行时可以具有的类型。这种类型可以在运行时更改,并且不需要在声明时指定。动态类型的优点是提供了更大的灵活性,使得程序员可以在运行时根据需要更改变量的类型。然而,动态类型也带来了运行时类型错误的潜在风险,因为它们可能在运行时引发异常或错误。

        类型是编程中的重要概念,用于描述变量和数据结构的特征和行为。它们提供了编译时的类型检查和运行时的灵活性,帮助程序员编写更可靠和可维护的代码。

2、案例

定义一个 dog 类:

class Dog:
"""小狗类定义"""
pass

在 Python 中,首字母大写的名称指的是类。

这个类定义是可以不带 ( ) ,括号中指定的父类型。

给 Dog 类定义属性:

class Dog:
"""小狗类定义"""
name: str = '小黑'
age: int = 3

编写对象,获取对象的属性:

d = Dog()
print(f"name:{d.name},age:{d.age}")

Python 中对于对象的属性是属于动态可变化的。

d = Dog()
d.age = 30
d.gender = '公'
print(f"name:{d.name},age:{d.age},gender:{d.gender}")

也可以给类型添加方法,方法就是定义再类中的函数。

def sit(self):
pass

每个与类相关联的方法调用都自动传递实参self,它是一个指向实例本身的引用,让实例能够访问类中的属 性和方法。

def sit(self):
print(f'小狗{self.name}蹲下了')

携带多个参数时,其他的参数放在 self 之后:

def eat(self, food: str):
print(f'小狗{self.name}吃下了{food}')

三、魔法方法

1、什么是魔法方法

        Python中,魔法方法是指那些以双下划线开头和结尾的方法,例如__add__()。这些方法通常用于运算符重载,当对类的某个操作时后端自动调用,而不需要自己直接调用。

如:当使用+将两个数字相加时,在类内部会调用__add__()方法。

2、基础部分

init 被用于初始化实例对象,new方法创建对象后就会调用该方法对实例的属性进行初始化。

class T:
def __init__(self):
self.name = 'sa'
self.pwd = '123'
t = T()

del 是析构方法,当对象的引用计数变为0时,这个对象会被销毁掉,此时就会调用del方法

class T:
def __del__(self):
print("对象被销毁")
t = T()
# t指向0,此前创建的T的实例被销毁
t = 0

call 允许一个类的实例像函数一样被调用,这是一个非常重要的特性

class T:
def __call__(self, *args, **kwargs):
print("类的实例可以像函数一样被调用")
t = T()
t()

len 是内置函数,len(t) 会调用实例的 len 方法

class T:
def __len__(self):
return 100
t = T()
print(len(t))

当被 str() 调用时, 调用该 str 方法

class T:
def __str__(self):
return f"我是{T.__name__}的一个实例"
t = T()
print(t)

bool 可用于逻辑判断部分

class T:
def __init__(self, age):
self.age = age
def __bool__(self):
return self.age > 18
print(bool(T(17) and T(19)))

3、比较操作

        比较操作可以是算术比较(例如等于、不等于、大于、小于、大于等于、小于等于)或者字典比较(例如键在字典中、键不在字典中)。

  1. 算术比较:

    • 等于 (==):eq(self, other)定义等于号的行为。
    • 不等于 (!=):ne(self, other)定义不等号的行为。
    • 大于 (>):gt(self, other)定义大于号的行为。
    • 小于 (<):lt(self, other)定义小于号的行为。
    • 大于等于 (>=):ge(self, other)定义大于等于号的行为。
    • 小于等于 (<=):le(self, other)定义小于等于号的行为。
  2. 字典比较:

    • 键在字典中 (key in dict):如果给定的键在字典中,则返回True,否则返回False。
    • 键不在字典中 (key not in dict):如果给定的键不在字典中,则返回True,否则返回False。

这些比较操作可以用于各种数据类型,包括数字、字符串、列表、元组、集合和字典等。

案例:

class Student:
def __init__(self, age):
self.age = age
def __lt__(self, other):
return self.age < other.age
def __eq__(self, other):
return self.age == other.age
s1 = Student(3)
s2 = Student(3)
print(s1 < s2)
print(s1 == s2)
print(s1 is s2)

4、容器类型

getitem, setitem, delitem getitem

        定义获取容器中指定元素的行为,相当于 self[key], setitem 定义设置容器中指定元素的行 为,相当于 self[key] = value, delitem 定义删除容器中指定元素的行为,相当于 del self[key]。

        字典就是实现了这三个魔法方法,才提供了以[]为基础的操作。

class MyData:
def __init__(self, data=None):
if data is None:
data = set()
self.data = data
def __getitem__(self, item):
return self.data.get(item)
def __setitem__(self, key, value):
self.data[key] = value
def __delitem__(self, key):
del self.data[key]
my_data = MyData()
my_data['name'] = '小刚'
my_data['age'] = 14
print(my_data['name'])
print(my_data['age'])

如果一个对象实现了 iter , 那么它就是一个可迭代对象

如果既实现 iter 又实现 next ,那么它就是一个迭代器

class Color(object):
def __init__(self):
self.index = -1
self.colors = ['red', 'white', 'black', 'green']
def __iter__(self):
self.index = -1
return self
def __next__(self):
self.index += 1
if self.index >= self.colors.__len__():
raise StopIteration
return self.colors[self.index]
color_object = Color()
# 遍历输出所有颜色
for color in color_object:
print(color)

5、属性管理

class Plane(object):
category = '飞机'
p1, p2 = Plane(), Plane()
# print(p1.category, p1.category)
# Plane.category = '拖拉机'
# print(p1.category, p2.category)
# print(p1.category, p1.category)
# p1.category = '拖拉机'
# print(p1.category, p2.category)

这里主要涉及到了类属性与对象属性实例属性的区别

python中,实例属性存储在一个字典(dict)中,对于属性的操作,都是在操作这个字典

p1.name = 'sa'
print(p1.__dict__)

也可以直接操作这个字典

p1.name = 'sa'
p1.__dict__['pwd'] = '123'
print(p1.pwd)

python中对象的属性为什么可以动态变化?

类属性同样是存储在字典中,只是这个字典是类的字典

可以通过类名和对象名来调用,但是只能通过类名修改,类属性对于该类对象是共享的

类的属性和方法存在在类的 dict 之中

p1, p2 = Plane(), Plane()
print(Plane.__dict__)

属性寻找规则: 先找实例的 dict ,再去查找类的 dict

案例:

class Dog:
name: str = '小黑'
age: int = 3
d1 = Dog()
d1.name = "大黄"
d2 = Dog()
Dog.name = "小白"
print(d1.name)
print(d2.name)

6、封装

        python中,使用一个下划线就等于向使用者宣布这个属性是私有的,但你仍然可以直接对其修改,单个 下划线更像是一种约定,而非技术上的强硬限制。即便使用了双下划线,也仍然有办法直接对其修改, 但这已经不是类所要解决的问题了,一个人执意破坏数据,他总是能找到办法。

案例:

class Stu:
def __init__(self, name, age):
self.__name = name
self.__age = age
def get_name(self):
return self.__name
def set_name(self, name):
self.__name = name
def get_age(self):
return self.__age
def set_age(self, age):
self.__age = age
stu1 = Stu('小明', 14)
stu2 = Stu('小红', 14)
print(stu1.get_name(), stu1.get_age())
print(stu2.get_name(), stu2.get_age())

还可以通过其他方式来实现封装

class Book:
def __init__(self, price):
self._price = price
@property
def price(self):
return self._price
@price.setter
def price(self, price):
if price > 0:
self._price = price
else:
raise Exception('价格不能为负数')
book = Book(58.5)
print(book.price)
book.price = -1
print(book.price)

使用 @property 装饰器修饰 price 方法后,你就可以像使用属性一样使用 price ,如果你希望可以对 price 进行赋值,那么需要用 @price.setter 装饰器再装饰一个方法,该方法完成对 _price 属性的赋值操 作。 我们以为自己直接操作了对象的属性,但其实我们是在使用类的方法,而且关键的是省去了调用方法时 的那对小括号

7、方法拓展

类方法 : 可以通过类名调用的方法

加上注解,修改参数为 cls (当前类),可以通过 cls 调用类的属性

@classmethod
def run(cls):
print(cls.__name)

静态方法 : 可以通过类名与对象来调用,但是无法使用类变量

@staticmethod
def say():
print("hello world")

8、继承

如果没有指定基类,会默认继承 object 类, object 是所有类的基类,它提供了一些常见方法, 比如 __str__,我们用案例来演示

class Dog(object):
pass

类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类

class A:
pass
class B:
pass
class C(A):
pass
class D(A, B):
pass

查看继承关系

print(D.__bases__)
print(D.__mro__)

继承可以继承属性与方法

class Father:
money = 100
class Son(Father):
pass
s = Son()
print(s.money)
class Father:
money = 100
def hello(self):
print("hello world")
class Son(Father):
pass
s = Son()
s.hello()
class Father:
money = 100
def hello(self):
print("hello world")
class Mother:
money = 200
def cook(self):
print("煮饭")
class Son(Father, Mother):
pass
s = Son()
print(s.money)
s.hello()
s.cook()

当属性和方法重复时,越往前优先级越高

9、多态

        多态是指同一个事件发生在不同的对象上会产生不同的结果,或者一个对象可以有多种形态。它是一种机制、一种能力,在类的继承中得以实现,在类的方法调用中得以体现。多态的存在可以消除类型之间的耦合关系,通过分离做什么和怎么做,将接口和实现分离开来。

        多态可以分为引用多态和方法多态。引用多态是指同一个对象变量可以指向多种实际类型的现象,而方法多态是指同一个方法可以被不同的对象调用产生不同的效果。

        多态是面向对象编程中非常重要的概念,它可以帮助我们编写更加灵活、可扩展和维护性更高的代码。

案例:

class Animal(object):
def play(self):
pass
class Tiger(Animal):
def play(self):
print("正在表演老虎吃人")
class Lion(Animal):
def play(self):
print("正在表演狮子跳火圈")
class Person(object):
def show(self, a: Animal):
print("动物开始表演了")
a.play()
p = Person()
tiger = Tiger()
lion = Lion()
# 让老虎表演
p.show(tiger)
# 让狮子表演
p.show(lion)

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2023年11月22日
下一篇 2023年11月22日

相关推荐