一、Python 中的面向对象
python 在设计时也是符合面向对象的原则的,和 Java 类似也存在类的创建,类的成员属性,成员方法,构造函数,类之间的继承等操作
不知道什么是面向对象?
合适我往期初步学习时的理解,希望能对你有用
面向对象和类 理解+案例
二、案例说明
简单的来一个,一个人的类,有名字,有年龄两个属性
有两个方法或者说函数,一个是吃,一个是喝
class Man:
# 名字
name="unKnown"
# 年龄
age=0
# 会吃
def eat(self):
print("快吃,要饿死了")
# 会喝
def drink(self):
print("快喝,要渴死了")
if __name__ == '__main__':
# 创建一个Man 类的对象
x = Man()
print(x.name)
print(x.age)
x.eat()
x.drink()
输出结果:
unKnown
0
快吃,要饿死了
快喝,要渴死了
self 参数
可以看看得出类的方法中都有一个参数 self,而且不给的话会直接报错
self 代表类的实例,而非类
类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。(可以改名,但为了所有人的阅读习惯最好不要改)
假设我们定义一个方法,可以如下:
def print_self(self):
print(self)
print(self.__class__)
输出:
分别输出了当前类的地址以及当前类的所在
<__main__.Man object at 0x0000018A0B5B3D68>
<class '__main__.Man'>
构造方法__init__即创建类的对象时的初始化方法
修改原代码添加两个参数,使得可以在创建时给属性赋值,如下:
class Man:
# 名字
name="unKnown"
# 年龄
age=0
# 构造方法 也就是创建类时的初始化方法
def __init__(self, name, age) -> None:
super().__init__() # 此行可以没有
self.name = name
self.age = age
# 会吃
def eat(self):
print("快吃,要饿死了")
# 会喝
def drink(self):
print("快喝,要渴死了")
if __name__ == '__main__':
x = Man("瘪三", 13)
print(x.name)
print(x.age)
x.eat()
x.drink()
输出:
瘪三
13
快吃,要饿死了
快喝,要渴死了
上面的构造方法写法是自动生成的,其实一般来说也可以这样
# 参数的多少可以自己控制 不一定全部
def __init__(self, name):
self.name = name
但需要注意的是,python 中没有提供对同名方法的重载,构造方法只能有一个,如果要控制,那么也只能从入参来控制内部不同的构造逻辑了
三、继承
python 中的继承更为简单一点,语法为
# 继承在其他模块(文件)中的类时要带上所在的模式名或者可以认为是路径名
class 当前类名(要继承的基础类):
例如我们再定义一个 超人类,继承上面的 Man:
超人有超能力的属性,有使用能力的方法
from pych1.Man import Man
class SuperMan(Man):
# 超能力的名字
superPowerName = "unknow"
# 构造函数
def __init__(self, superPowerName, name, age) -> None:
super().__init__(name, age) # super() 这里指代基类(java中的父类,也就是Man 这个基类 这里调用了父类的构造方法,完成部分属性的赋值)
self.superPowerName = superPowerName
# 发动能力
def usePower(self):
return "我要启动了,快闪开!"
if __name__ == '__main__':
m1 = SuperMan("自爆", "不知名", 80)
print("姓名:{},年龄:{},能力:{},最后的遗言:{}".format(m1.name, m1.age, m1.superPowerName, m1.usePower()))
文件结构
输出结果:
姓名:不知名,年龄:80,能力:自爆,最后的遗言:我要启动了,快闪开!
从上面结果看的出,和java 等面向对象语言一致
子类(派生类 DerivedClassName)会继承父类(基类 BaseClassName)的属性和方法。
四、多继承
基础语法:
class DerivedClassName(Base1, Base2, Base3):
需要注意圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索 即方法在子类中未找到时,从左到右查找父类中是否包含方法。
五、方法重写
子类虽然继承了父类的方法,但是不能满足子类想要的结果,那么可以在子类中再次重写该方法,最终子类调用时就会走自己的实现了
例如刚才的超人类我重写了吃的方法
from pych1.Man import Man
class SuperMan(Man):
# 超能力的名字
superPowerName = "unknow"
# 构造函数
def __init__(self, superPowerName, name, age) -> None:
super().__init__(name, age)
self.superPowerName = superPowerName
# 发动能力
def usePower(self):
return "我要启动了,快闪开!"
def eat(self):
print("超人不需要吃饭")
if __name__ == '__main__':
m1 = SuperMan("自爆", "不知名", 80)
print("姓名:{},年龄:{},能力:{},最后的遗言:{}".format(m1.name, m1.age, m1.superPowerName, m1.usePower()))
m1.eat()
输出:
姓名:不知名,年龄:80,能力:自爆,最后的遗言:我要启动了,快闪开!
超人不需要吃饭
六、私有方法,私有属性
每个人都有隐私,类也不例外
私有方法和私有属性都可以通过 在名字前加两个下划线表示
类的私有属性
__private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs。
类的私有方法
__private_method:两个下划线开头,声明该方法为私有方法,只能在类的内部调用 ,不能在类的外部调用。self.__private_methods。
例如我在Man 中添加了私有属性如下
# 名字
name="unKnown"
# 年龄
age=0
# 私有属性
__tall = "不告诉你"
此时从外部访问时会出现如下:
class Man:
# 名字
name="unKnown"
# 年龄
age=0
__tall = "不告诉你"
# 构造方法 也就是创建类时的初始化方法
def __init__(self, name, age) -> None:
super().__init__() # 此行可以没有
self.name = name
self.age = age
# 会吃
def eat(self):
print("快吃,要饿死了")
# 会喝
def drink(self):
print("快喝,要渴死了")
def print_self(self):
print(self)
print(self.__class__)
def self_ss(self):
print(self.__tall)
if __name__ == '__main__':
x = Man("rose", 2)
print(x.name)
print(x.age)
x.eat()
x.drink()
x.print_self()
print(x.__tall)
输出:
Traceback (most recent call last):
File "XXXXXXXXX.py", line 40, in <module>
print(x.__tall)
AttributeError: 'Man' object has no attribute '__tall'
rose
2
快吃,要饿死了
快喝,要渴死了
<__main__.Man object at 0x0000027590E3BEF0>
<class '__main__.Man'>
被提示 __tail 不存在
# 但假设我如下调用
o1 = Man("jack", 2)
# print(o1.__tall)
o1.self_ss()
输出:
姓名:不知名,年龄:80,能力:自爆,最后的遗言:我要启动了,快闪开!
超人不需要吃饭
不告诉你
看得出 在类内部被引用时是可以正常访问的
类的专有方法:
方法名 | 描述 |
---|---|
__init__ | 构造函数,在生成对象时调用 |
__del__ | 析构函数,释放对象时使用 |
__repr__ | 打印,转换 |
__setitem__ | 按照索引赋值 |
__getitem__ | 按照索引获取值 |
__len__ | 获得长度 |
__cmp__ | 比较运算 |
__call__ | 函数调用 |
__add__ | 加运算 |
__sub__ | 减运算 |
__mul__ | 乘运算 |
__truediv__ | 除运算 |
__mod__ | 求余运算 |
__pow__ | 乘方 |
同样的 以上方法也可以重写