一、创建和使用
class Restaurant:
"""创建一个餐厅类"""
def __init__(self, restaurant_name, cuisine_type):
""""初始化自身的信息"""
self.restaurant_name=restaurant_name
self.cuisine_type=cuisine_type
def describe_restaurant(self):
"""显示餐厅对象的信息"""
print(f"name:{self.restaurant_name},type:{self.cuisine_type}")
def open_restaurant(self):
print("restaurant is opening")
""""1.创建一个餐厅对象"""
restaurant=Restaurant("abcd","Chinese food")
""""2.打印餐厅的名字"""
print(restaurant.restaurant_name)
""""3.打印餐厅的类型"""
print(restaurant.cuisine_type)
""""4.调用餐厅的函数"""
restaurant.describe_restaurant()
""""5.调用餐厅的函数"""
restaurant.open_restaurant()
执行结果:
1)__init__()方法
__init__()方法会在创建类对象时自动调用,通常利用该方法对对象的属性进行初始化。
2)方法中的参数(self)
在每个方法中,第一个参数应该是self,它会自动把当前对象当成实参传入方法。通过使用self.xxx可以调用当前对象中的一些属性
3)参数中也可以指定默认值
与函数的默认值一样,方法中形参也可以指定默认值
二、访问权限
Pthon的类中只有两种访问权限,公有类型和私有类型
1)公有类型
公有类型分为公有变量和公有方法,他们都是通过点操作符( . )来进行访问
class Student:
#公有变量定义方式1
age = 18
def __init__(self,name):
#公有变量定义方式2
self.name = name
#公有方法定义方式
def get_student(self):
print(f"学生:{self.name},年龄:{self.age},学历:{self.graduation}")
person = Student("None")
#可在类外直接修改公有变量
person.name = "Xiao Ming"
person.age = 25
#可在类外直接调用公有方法
person.get_student() #错误调用,因为该函数中的graduation变量找不到
#公有变量定义方式3
person.graduation = "Middle"
#可在类外直接调用公有方法
person.get_student()
#上面代码结果为:
#学生:Xiao Ming,年龄:25,学历:Middle
可以看出,公有变量的定义方式有三种:
- 在类中直接定义一个变量
- 在类中的函数使用self.xxx定义一个变量
- 在类的具体对象中(类外)使用点操作符定义一个变量
【注意】如果类成员函数使用到了类中尚未定义的变量时,记得在调用该函数时先在类外定义该变量,如上述代码的graduation变量
2)私有类型
私有类型分为私有变量和私有方法,他们的定义方式为在开头使用两个下划线__,他们只能在内部调用,不能在外部调用
class Student:
#私有变量定义方式1
__age = 18
def __init__(self,name,graduation):
#私有变量定义方式2
self.__name = name
self.__graduation = graduation
#私有方法定义方法
def __get_student(self):
print(f"学生:{self.__name},年龄:{self.__age},学历:{self.__graduation}")
def get_student(self):
self.__get_student()
person = Student("Xiao Ming","Middle")
#尝试改变类中的私有变量
person.__age = 25
person.get_student()
#上面代码结果为(发现年龄并没有发生变化):
#学生:Xiao Ming,年龄:18,学历:Middle
【注意】在上述代码中,我们尝试在类外访问类的私有变量,但是通过输出我们发现该变量并没有改变,说明无法直接调用私有类型,因此我们通常都是在类中利用公有方法访问私有类型
3)访问私有类型
在Python中,其实也可以直接访问私有类型。上述代码中,不能直接访问__age的原因是因为Python对外把__age变量改写成了_Student__age,所以也可以在外部通过_Student__age来访问类中的__age变量。(只是希望你看到这个变量是知道这是私有类型)
class Student:
__age = 18
def __init__(self,name,graduation):
self.__name = name
self.__graduation = graduation
def __get_student(self):
print(f"学生:{self.__name},年龄:{self.__age},学历:{self.__graduation}")
def get_student(self):
self.__get_student()
person = Student("Xiao Ming","Middle")
person.__age = 25
print(person.__age)
person.get_student()
person._Student__age = 30
print(person._Student__age)
person.get_student()
#上面代码结果为:
#25
#学生:Xiao Ming,年龄:18,学历:Middle
#30
#学生:Xiao Ming,年龄:30,学历:Middle
- 我们可以发现,如果在类外尝试用person.__person访问类中__person变量时会失败,反而会为类新定义了一个公有变量(并且该公有变量只能在类外通过点操作符才可以访问,在类中使用self.__age访问的会是私有变量)
- 如果在类外用person._Student__person访问类中的__person变量则成功。但是强烈建议你不要这么干,因为不同版本的Python解释器可能会把__age改成不同的变量名
三、类的专用方法
Python除了自定义私有变量和方法外,还可以定义专有方法。专有方法是在特殊情况下或使用特殊语法时由python调用的,而不是像普通方法一样在代码中直接调用。在Python中,如果看到如__XXX__的变量或函数名时要注意,这在Python中是有特殊用途的:
方法名 | 用途 |
---|---|
__init__ | 构造函数,在生成对象时调用 |
__del__ | 析构函数,释放对象时调用 |
__repr__ | 打印,转换 |
__setitem__ | 按照索引赋值 |
__getitem__ | 按照索引获取值 |
__len__ | 获得长度 |
__cmp__ | 比较运算 |
__call__ | 函数调用 |
__add__ | 加运算 |
__sub__ | 减运算 |
__mul__ | 乘运算 |
__div__ | 除运算 |
__mod__ | 求余运算 |
__pow__ | 乘方 |
1)__str__和__repr__
1.__str__()
参数:只有self
作用:把一个类的对象在输出使变成一自定义字符串
返回值:需要返回一个字符串
class StudentA:
def __init__(self,name):
self.name = name
class StudentB:
def __init__(self,name):
self.name = name
def __str__(self):
return "学生的姓名:%s"%self.name
personA = StudentA("Xiao Ming")
print(personA)
personB = StudentB("Xiao Liu")
print(personB)
#执行结果:
#<__main__.Student object at 0x00000000277F6C0>
#学生的姓名:Xiao Liu
【注意】从上述代码得知,如果直接输出一个类的对象,会得到特殊字符串(程序开发者所用)
2.__repr__()
参数:只有self
作用:把一个类的对象在交互模式下输出时变成一自定义字符串
返回值:需要返回一个字符串
>>> class StudentA:
... def __init__(self,name):
... self.name = name
... def __str__(self):
... return "学生的姓名:%s"%self.name
...
>>> class StudentB:
... def __init__(self,name):
... self.name = name
... def __str__(self):
... return "学生的姓名:%s"%self.name
... def __repr__(self):
... return "调试—学生的姓名:%s"%self.name
...
>>> personA = StudentA("Xiao Ming")
>>> personA
<__main__.StudentA object at 0x0000000002C5EE48>
>>> print(personA)
学生的姓名:Xiao Ming
>>> personB = StudentB("Xiao Liu")
>>> personB
调试—学生的姓名:Xiao Liu
>>> print(personB)
学生的姓名:Xiao Liu
【注意】
- __str__() 返回用户看到的字符串,而 __repr__() 返回程序开发者看到的字符串。也就是__repr__() 是为调试服务的
- 除了直接定义__repr__函数外,也可以使用__repr__=__str__定义repr函数,让调试得到的字符串和输出的到的字符串一致
3.特殊情况
class Student:
def __init__(self,name,age):
self.name = name
self.age = age
def __str__(self):
return f"({self.name},{self.age})"
a = Student('a',18)
b = Student('b',25)
c = Student('c',15)
students = [a,b,c]
print(students)
print(students[0])
执行结果
可以看出,当整个list输出时,Student对象返回的是一个用于调试的值,而具体访问列表中某个Student对象时,会调用__str__函数。因此,如果我们想无论哪种情况都输出一致时,记得加上__repr__ = __str__
2)__iter__和__next__
1.__iter__()
参数:只有self
作用:将一个类用于 for ... in 循环,类似 list 或 tuple 一样。
返回值:返回一个迭代对象,可以返回自身(self就是一个迭代对象)
2.__next__()
参数:只有self
作用:for 循环会不断调用__iter__()返回的迭代对象的__next__() 方法,获得循环的下一个值,直到遇到 StopIteration 错误时退出循环
返回值:可以返回任意值
class Fib:
def __init__(self):
self.a = 0
def __iter__(self):
print("run __iter__")
return self
def __next__(self):
self.a += 1
if self.a > 5:
raise StopIteration();
return f"run __next__:{self.a}"
fib = Fib()
for n in fib:
print(n)
#执行结果
#run __iter__
#run __next__:1
#run __next__:2
#run __next__:3
#run __next__:4
#run __next__:5
【注意】在for...in循环调用后,__iter__只会调用一次,而__next__调用次数与自身实现相关,当遇到StopIteration()会马上停止循环
3)__getitem__和__setitem__
类虽然能够作用于 for 循环,和 list 有点像,但是不能将它当成 list 使用。若要像list一样按照索引来获取元素或改变元素值,需要实现__getitem__()方法和__setitem__()方法
1.__getitem__()
参数:self和索引值
作用:使类像list一样按照索引来获取元素,在对象使用[]运算符时会调用该函数
返回值:可以返回任意值,但最好是会根据索引值变化
2.__setitem__()
参数:self,索引值,用于赋值的参数(实参为等号左边的值)
作用:使类像list一样按照索引来改变元素值,在对对象某个按索引获取的值赋值时会调用该函数,如a[2] = 10
返回值:可以返回任意值
class Student:
def __init__(self):
self.student = ['a','b','c','d','e','f']
def __getitem__(self,n):
return f"run __getitem__:student {self.student[n]}"
def __setitem__(self,n,str):
self.student[n] = str
print("run __setitem__")
a = Student()
print(a[2])
a[2] = "xiao ming"
print(a[2])
#执行结果
#run __getitem__:student c
#("run __setitem__")
#run __getitem__:student xiao ming
【注意】在__setitem__中不可以使用self[n]进行赋值,这种情况会产生无限递归,递归调用__getitem__和__setitem__
4)__len__
参数:只有self
作用:对对象使用len()方法时,调用该__len__()方法
返回值:必须返回一个整形
class Student:
def __init__(self):
self.student = ['a','b','c','d','e','f']
def __len__(self):
print("run __len__")
return len(self.student)
a = Student()
print(len(a))
#执行结果
#run __len__
#6
5)__cmp__
使用python内置的sorted()函数里默认的cmp函数对其排序。
参数:self和用于比较的参数
作用:对对象进行任何的比较时,都会调用该函数
返回值:返回-1,1,或0
class Student:
def __init__(self,name,age):
self.name = name
self.age = age
def __cmp__(self,student):
if self.age < student.age:
return -1
elif self.age > student.age:
return 1
else:
return 0
def __str__(self):
return f"({self.name},{self.age})"
__repr__ = __str__
a = Student('a',18)
b = Student('b',25)
c = Student('c',15)
students = [a,b,c]
print(students)
sorted(students)
#执行结果
#[(a,18),(b,25),(c,15)]
#[(c,15),(a,18),(b,25)]
【注意】在Python3中,该函数已删除,而使用operator模块中的函数代替
函数 | 作用 |
---|---|
__lt__(a,b) | a<b,从第一个数字或字母(ASCII)比大小 |
__le__(a,b) | a<=b |
__eq__(a,b) | a==b,字母完全一样,返回True, |
__ne__(a,b) | a!=b |
__gt__(a,b) | a>b |
__ge__(a,b) | a>=b |
四、继承
class Car:
"""一个Car类"""
def __init__(self,make,model,year):
self.make=make
self.model=model
self.year=year
self.odometer_reading=0
def update_odometer(self,mileage):
if mileage>=self.odometer_reading:
self.odometer_reading=mileage
else:
print("You can't roll back an odometer!")
def increament_odometer(self,miles):
self.odometer_reading+=miles
class Battery:
"""一个电池类"""
def __init__(self,battery_size=75):
self.battery_size=battery_size
def get_range(self):
if self.battery_size==75:
range=260
elif self.battery_size==100:
range=315
print(f"This car can go about {range} miles on a full charge.")
class ElectriCar(Car):
"""一个电动车类,继承自Car类,并且有一个属性为电池类对象"""
def __init__(self, make, model, year):
super().__init__(make, model, year)
self.battery=Battery()
def update_battery(self):
if self.battery.battery_size!=100:
self.battery.battery_size=100
elcar=ElectriCar("Toyota","A5","2020")
elcar.battery.get_range()
elcar.update_battery()
elcar.battery.get_range()
执行结果:
1)子类的__init__()方法
在创建一个子类对象时,如果你想子类对象也能调用父类中的方法和属性时,需要用到特殊函数super()。
super().__init__(make, model, year)会调用父类的__init__()方法