1.类和对象
是不是很熟悉?和Java一样,在Python中,都可以把万物看成(封装成)对象。它俩都是面向对象编程
1.1 查看对象数据类型
a = 10
b = 9.8
c = 'hello'
print(type(a))
print(type(b))
print(type(c))
运行结果:
D:\Python_Home\venv\Scripts\python.exe D:\Python_Home\chap9\示例9-1查看对象的数据类型.py
<class 'int'>
<class 'float'>
<class 'str'>
1.2 自定义数据类型
# 编写一个 Person
class Person():
pass
# 编写一个 Cat 类
class Cat():
pass
# 编写一个 Dog 类
class Dog:
pass
class Student:
pass
1.3 创建自定义类型的对象
# 编写一个 Person
class Person():
pass
# 编写一个 Cat 类
class Cat():
pass
# 编写一个 Dog 类
class Dog:
pass
class Student:
pass
#创建类的对象
# 对象名=类名()
# 创建一个 Person 类型的对象
per=Person()
c=Cat()
d=Dog()
stu=Student()
print(type(per))
print(type(c))
print(type(d))
print(type(stu))
运行结果:
D:\Python_Home\venv\Scripts\python.exe D:\Python_Home\chap9\示例9-3创建自定义类型的对象.py
<class '__main__.Person'>
<class '__main__.Cat'>
<class '__main__.Dog'>
<class '__main__.Student'>
1.4 类属性和实例属性的定义
class Student:
# 类属性: 定义在类中,方法外的变量
school = '北京大学'
# 初始方法方法
def __int__(self, xm, age): # xm,age 是方法的参数,是局部变量,作用域 是 整个 __init__方法
self.name = xm # 左侧是实例属性, xm是局部变量,将局部变量的值 xm 赋值给实例属性 self.name
self.age = age # 实例的名称和局部变量的名称可以相同
1.5 类的组成
class Student:
# 类属性: 定义在类中,方法外的变量
school = '北京大学'
# 初始方法方法
def __init__(self, xm, age): # xm,age 是方法的参数,是局部变量,作用域 是 整个 __init__方法
self.name = xm # 左侧是实例属性, xm是局部变量,将局部变量的值 xm 赋值给实例属性 self.name
self.age = age # 实例的名称和局部变量的名称可以相同
# 定义在类中的函数,成为方法,自带一个 参数 self
def show(self):
print(f'我叫:{self.name},今年{self.age}岁了')
# 静态方法
@staticmethod
def sm():
# print(self.name)
# self.show()
print('这是一个静态方法,不能调用实例属性,也不能调用实例方法')
# 类方法
@classmethod
def cm(cls): # cls--> class 的简写
print('这是一个类方法,不能调用实例属性,也不能调用实例方法')
# 创建类的对象
stu = Student('Jarvis', 21) # 为什么传了两个参数?因为 __init__ 方法中,有两个形参, self 是自带的参数,无需手动传入
# 实例属性,使用对象名 进行打点调用
print(stu.name,stu.age)
#类属性,直接使用 类名,打点调用
print(Student.school)
#示例方法,使用对象名 打点调用
stu.show()
# 类方法 @classmethod 进行修饰的方法,直接使用类名打点调用
Student.cm()
# 静态方法 ,直接使用 类名打点调用
Student.sm()
1.6 编写学生类创建4个学生对象
class Student:
# 类属性: 定义在类中,方法外的变量
school = '北京大学'
# 初始方法方法
def __init__(self, xm, age): # xm,age 是方法的参数,是局部变量,作用域 是 整个 __init__方法
self.name = xm # 左侧是实例属性, xm是局部变量,将局部变量的值 xm 赋值给实例属性 self.name
self.age = age # 实例的名称和局部变量的名称可以相同
# 定义在类中的函数,成为方法,自带一个 参数 self
def show(self):
print(f'我叫:{self.name},今年{self.age}岁了')
# 根据 "图纸" 可以创建出 N多个对象
stu = Student('jarvis', 18)
stu2 = Student('zhangsan', 19)
stu3 = Student('lisi', 20)
stu4 = Student('wangwu', 21)
print(type(stu))
print(type(stu2))
print(type(stu3))
print(type(stu4))
Student.school = '清华大学' # 给类的属性赋值
# 将学生对象存储到列表中
lst = [stu, stu2, stu3, stu4] # 列表中的元素是 Student类型的对象
for item in lst:
item.show() # 对象打点调用实例方法
运行结果:
D:\Python_Home\venv\Scripts\python.exe D:\Python_Home\chap9\示例9-6编写学生类创建4个学生对象.py
<class '__main__.Student'>
<class '__main__.Student'>
<class '__main__.Student'>
<class '__main__.Student'>
我叫:jarvis,今年18岁了
我叫:zhangsan,今年19岁了
我叫:lisi,今年20岁了
我叫:wangwu,今年21岁了
1.7 动态绑定属性和方法
代码:
class Student:
# 类属性: 定义在类中,方法外的变量
school = '北京大学'
# 初始方法方法
def __init__(self, xm, age): # xm,age 是方法的参数,是局部变量,作用域 是 整个 __init__方法
self.name = xm # 左侧是实例属性, xm是局部变量,将局部变量的值 xm 赋值给实例属性 self.name
self.age = age # 实例的名称和局部变量的名称可以相同
# 定义在类中的函数,成为方法,自带一个 参数 self
def show(self):
print(f'我叫:{self.name},今年{self.age}岁了')
# 创建两个 Student 类型的对象
stu = Student('jarvis', 18)
stu2 = Student('zhangsan', 19)
print(stu.name,stu.age)
print(stu2.name,stu2.age)
# 为 stu2 动态绑定一个实例属性
stu2.gender='男'
print(stu2.name,stu2.age,stu2.gender)
#动态绑定 方法
def introduce():
print('我是一个普通的函数,我被动态绑定成了 stu2对象的方法')
stu2.fun=introduce() #函数的一个赋值,(记住方法千万不能加小括号,加小括号就是调用了)
# fun就是stu2对象的方法
#调用
stu2.fun
运行结果:
D:\Python_Home\venv\Scripts\python.exe D:\Python_Home\chap9\示例9-7动态绑定属性和方法.py
jarvis 18
zhangsan 19
zhangsan 19 男
我是一个普通的函数,我被动态绑定成了 stu2对象的方法
2.面向对象三大特征
封装,继承,多态
是的,和Java一样
2.1 封装-权限控制
代码:
class Student():
#首位双下划线
def __init__(self,name,age,gender):
self._name=name # self._name 受保护的,只能本类和子类访问
self.__age=age # self.__age 表示私有的,只能类本身去访问
self.gender=gender #普通的实例属性,类的内部,外部,及子类都可以访问
def _fun1(self):
print('子类及本身可以访问')
def __fun2(self):
print('只有定义的类可以访问')
def show(self): #普通的实例方法
self._fun1() #类本身访问受保护的方法
self.__fun2() #类本身访问私有方法
print(self._name) #受保护的实例属性
print(self.__age) #私有的实例属性
#创建一个学生对象
stu=Student('jarvis',23,'男')
# 类的外部
print(stu._name)
#print(stu.__age) # AttributeError: 'Student' object has no attribute '__age'. Did you mean: '_name'?
#调用受保护的实例方法
stu._fun1()
#私有方法
#stu.__fun2() #AttributeError: 'Student' object has no attribute '__fun2'. Did you mean: '_fun1'?
# 私有的实例属性 和 方法是真的不能访问吗?
私有的实例属性 和 方法是真的不能访问吗?!!!
print(stu._Student__age) #为什么可以这样访问呢??
stu._Student__fun2()
为什么可以这样访问呢??
我们可以用dir()方法查看类所有的方法
print(dir(stu))
运行结果:
D:\Python_Home\venv\Scripts\python.exe D:\Python_Home\chap9\示例9-8权限控制.py
jarvis
子类及本身可以访问
23
只有定义的类可以访问
['_Student__age', '_Student__fun2', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_fun1', '_name', 'gender', 'show']
2.1.1 属性的设置
class Student:
def __init__(self,name,gender):
self.name=name
self.__gender=gender # self.__gender 是私有的实例属性
# 使用 @property 修饰方法,将方法转成属性使用
@property
def gender(self):
return self.__gender
#将我们的 gender 这个属性设置为可写属性
@gender.setter
def gender(self,value):
if value!='男' and value!='女':
print('性别有误,已将性别默认设置为男')
self.__gender='男'
else:
self.__gender=value
#
stu=Student('zhangsan','男')
print(stu.name,'性别是:',stu.gender) # stu.gender 就会去执行 stu.gender()
# 尝试修改属性值
#stu.gender='女' # AttributeError: property 'gender' of 'Student' object has no setter
stu.gender='其他'
print('这个学生的性别是:',stu.gender)
运行结果:
D:\Python_Home\venv\Scripts\python.exe D:\Python_Home\chap9\示例9-9属性的设置.py
zhangsan 性别是: 男
性别有误,已将性别默认设置为男
这个学生的性别是: 男
2.2 继承
- 继承
代码:
class Person: # 默认继承了 object
def __init__(self, name, age):
self.name = name
self.age = age
def show(self):
print(f'大家好,我叫:{self.name},我今年:{self.age}岁')
# Student 继承 Person 类
class Student(Person):
# 编写初始化的方法
def __init__(self, name, age, stuno):
super().__init__(name, age) # 调用父类的初始化方法
self.stuno = stuno
# Doctor 继承Person 类
class Doctor(Person):
# 编写初始化方法
def __init__(self, name, age, department):
super().__init__(name, age)
self.department = department
# 创建第一个子类对象
stu = Student('Jarvis', 20, '1001')
stu.show()
doctor = Doctor('zhangsan', 35, '外科')
doctor.show()
运行结果:
D:\Python_Home\venv\Scripts\python.exe D:\Python_Home\chap9\示例9-10继承.py
大家好,我叫:Jarvis,我今年:20岁
大家好,我叫:zhangsan,我今年:35岁
- 多继承
class FatherA():
def __init__(self, name):
self.name = name
def showA(self):
print('父类A中的方法')
class FatherB():
def __init__(self, age):
self.age = age
def showB(self):
print('父类B中的方法')
class Son(FatherA, FatherB):
def __init__(self, name, age, gender):
# 需要调用两个父类的初始化方法
FatherA.__init__(self, name)
FatherB.__init__(self, age)
self.gender = gender
son=Son('Jarvis',22,'男') # 调用Son类中的 __init__执行
son.showA()
son.showB()
运行结果:
D:\Python_Home\venv\Scripts\python.exe D:\Python_Home\chap9\示例9-11多继承.py
父类A中的方法
父类B中的方法
方法重写
class Person: # 默认继承了 object
def __init__(self, name, age):
self.name = name
self.age = age
def show(self):
print(f'大家好,我叫:{self.name},我今年:{self.age}岁')
# Student 继承 Person 类
class Student(Person):
# 编写初始化的方法
def __init__(self, name, age, stuno):
super().__init__(name, age) # 调用父类的初始化方法
self.stuno = stuno
def show(self):
# 调用父类中的方法
super().show()
print(f'我来自XXX大学,我的学号是:{self.stuno}')
# Doctor 继承Person 类
class Doctor(Person):
# 编写初始化方法
def __init__(self, name, age, department):
super().__init__(name, age)
self.department = department
def show(self):
#super().show() #调用父类中的show方法
print(f'大家好,我叫:{self.name},我今年{self.age},我的工作科室是:{self.department}')
# 创建第一个子类对象
stu = Student('Jarvis', 20, '1001')
stu.show()
doctor = Doctor('zhangsan', 35, '外科')
doctor.show() # 调用子类自己的 show()方法
2.3 多态
class Person():
def eat(self):
print('人吃,五谷杂粮')
class Cat():
def eat(self):
print('猫,喜欢吃鱼')
class Dog():
def eat(self):
print('狗,喜欢啃骨头')
# 这三个类中都有一个同名的方法 ,eat
# 编写函数
def fun(obj): # obj 是函数的形式参数,在定义处知道这个形参的数据类型吗?
obj.eat() # 通过变量 obj(对象) 调用eat方法
# 创建三个类的对象
per = Person()
cat = Cat()
dog = Dog()
# 调用 fun函数
fun(per) #Python中的多态,不关心对象的数据类型,只关心对象是否具有同名方法
fun(cat)
fun(dog)
3.Object 类
3.1查看指定对象的属性
class Person(object):
def __init__(self,name,age):
self.name=name
self.age=age
def show(self):
print(f'大家好,我叫:{self.name},我今年:{self.age}')
# 创建Person类的对象
per=Person('Jarvis',20) # 创建对象的时候会自动调用 __init__方法
print(dir(per))
print(per) #自动调用了 __str__方法
运行结果:
D:\Python_Home\venv\Scripts\python.exe D:\Python_Home\chap9\示例9-14查看指定对象的属性.py
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'show']
<__main__.Person object at 0x000001E0F81ECA10>
3.2 ‘__str__方法’
重写之前:
class Person(object):
def __init__(self,name,age):
self.name=name
self.age=age
per=Person('zhangsan',20)
print(per)
结果:
D:\Python_Home\venv\Scripts\python.exe D:\Python_Home\chap9\示例9-15__str__方法重写之前.py
<__main__.Person object at 0x000001303139FF50>
重写之后:
class Person(object):
def __init__(self,name,age):
self.name=name
self.age=age
# 方法重写
def __str__(self):
return '这是一个人类,具有name和age两个实例属性' #返回值是一个字符串
per=Person('zhangsan',20)
print(per) #还是内存地址码?不是,__str__方法中的内容, 直接输出对象名,实际上是调用了 __str__方法
print(per.__str__())
结果:
D:\Python_Home\venv\Scripts\python.exe D:\Python_Home\chap9\示例9-16__str__方法重写之后.py
这是一个人类,具有name和age两个实例属性
这是一个人类,具有name和age两个实例属性
3.3 特殊方法
a=10
b=20
print(dir(a)) #Python中一切皆对象
运行结果:
D:\Python_Home\venv\Scripts\python.exe D:\Python_Home\chap9\示例9-17特殊方法.py
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__getstate__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'as_integer_ratio', 'bit_count', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']
可以看到有很多方法,比如 ‘__add__加法’ 、‘__sub__减法’
我们可以用一下这些方法
a=10
b=20
print(dir(a)) #Python中一切皆对象
print(a+b) #执行加法运算
print(a.__add__(b))
print(a.__sub__(b)) #执行减法运算
print(f'{a}<{b}吗?',a.__lt__(b))
print(f'{a}<={b}吗?',a.__le__(b))
print(f'{a}=={b}吗?',a.__eq__(b))
print('*'*40)
print(f'{a}>{b}吗?',a.__gt__(b))
print(f'{a}>={b}吗?',a.__ge__(b))
print(f'{a}!={b}吗?',a.__ne__(b))
print('-'*40)
print(a.__mul__(b)) #乘法
print(a.__truediv__(b)) #除法
print(a.__mod__(b)) #取余