类的写法
class 类名:
def _init_(self,参数1,参数2):#构造函数
……
def 成员函数1(self,参数1,参数2):
......
def 成员函数2(self,参数1,参数2):
......
#所有成员函数,第一个参数都是self
class rectangle:
def __init__(self,w,h):
self.w,self.h=w,h
def area(self):
return self.w*self.h
def perimeter(self):
return 2*(self.w+self.h)
def main():
w,h=map(int,input().split())
rec=rectangle(w,h)#生成rectangle对象
print(rec.area())
print(rec.perimeter())
rec.w,rec.h=10,5#在main里面,对对象的成员变量直接进行修改
print(rec.area())
print(rec.perimeter())
main()
对象比较
python中所有的类,包括自定义的类,都有__ eq __方法
x==y的值,就是x.__ eq __(y)的值
如果x.__ eq __ (y)没定义,则y. __ eq __(x)
都没有定义则不适用
- a!=b <=> a.__ ne __(b)
- a<b <=> a.__ lt __(b)
- a>b <=> a.__ gt__ (b)
- a<=b <=> a.__ le __(b)
- a>=b <=> a.__ ge __(b)
自定义对象的比较
- 默认情况下,类的__ eq __方法,功能是判断两个对象的id是否相同
- 即是不是同一块地址,功能等同于is
- 默认情况下,自定义类的对象不能比较大小
因此,为实现自定义类的比较能力,应该进行重载
#二维平面点的重载
class point:
def __init__(self,x,y=0):#y在这里是缺省参数
self.x,self.y=x,y
def __eq__(self,other):
return self.x==other.x and self.y==other.y
def __lt__(self,other):#相当于<号的重载
if self.x!=other.x:
return self.x <other.x
else:
return self.y<other.y
def main():
m,n,a,b=map(int,input().split())
p1=point(m,n)
p2=point(a,b)
print(p1==p2)
print(p1<p2)
lst=[p1,p2,point(-2,3),point(7,8),point(5,9),point(1,1)]
lst.sort()#实现排序中,使用<号的重载
for a in lst:
print(a.x,a.y)
main()
对象输出
自定义类重写__ str __方法,将对象转为字符串(类型转换重载)
#二维平面点的重载
class point:
def __init__(self,x,y=0):#y在这里是缺省参数
self.x,self.y=x,y
def __str__(self):
return ("[%d,%d]"%(self.x,self.y))
print(str(point(2,4)))
#>>[2,4]
print(point(1,2))#也会自动将该对象类型,转换成可输出的字符串类型
#>>[1,2]
继承和派生
对具有共同特点的类,避免重复写
#派生类写法
class 类名(基类):
......
import datetime
class Student:
def __init__(self,id,name,gender,birthYear):
self.id,self.name,self.gender,self.birthYear=id,name,gender,birthYear
def __str__(self):#相当于流输出运算符的重载
return ("Name:%s\nID:%s\nBirth Year:%d\nGender:%s\nAge:%d\n"%(self.name,self.id,self.birthYear,self.gender,self.countAge()))
def countAge(self):
return datetime.datetime.now().year-self.birthYear
class undergraduateStudent(Student):#本科生类,继承自学生,多增加一个系(department)成员变量
def __init__(self,id,name,gender,birthYear,department):
Student.__init__(self,id,name,gender,birthYear)
self.department=department
def qualifiedForBaoyan(self):
print(self.name+" is qualified for BaoYan")
def __str__(self):
return Student.__str__(self)+"department:%s\n"%(self.department)
stu=Student("202100202098","Caaaaan","Boys",2002)
print(stu)
#>>Name:Caaaaan
#>>ID:202100202098
#>>Birth Year:2002
#>>Gender:Boys
#>>Age:20
stu2=undergraduateStudent("2021002020998","Caananan","Boys",2003,"Computer")
#>>Name:Caananan
#>>ID:2021002020998
#>>Birth Year:2003
#>>Gender:Boys
#>>Age:19
#>>department:Computer
print(stu2)
object类
python所有的类都有object类的派生类
因而都具有object类的各种属性
class A:
def func(self):
pass
print(dir(A))
#>>['__class__', '__delattr__', '__dict__',
# '__dir__', '__doc__', '__eq__', '__format__',
# '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
# '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__',
# '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
# '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'func']
静态属性和静态方法
- 静态属性被所有对象所共享,一共只有一份
- 静态方法不是作用在具体的某个对象上的,不能访问非静态属性
- 静态属性和静态方法这种机制存在的目的,就是为了少些全局变量和全局函数
class employee:
totalSalary=0 #静态属性,记录发给员工的工资总数
def __init__(self,name,income):
self.name,self.income=name,income
def pay(self,salary):
self.income+=salary
employee.totalSalary+=salary
@staticmethod #不设置成静态函数,会报错
def printTotalSalary():
print(employee.totalSalary)
e1=employee("Can",1500)
e1.pay(2500)
e1.printTotalSalary()
对象作为集合的元素和字典的键
“可哈希”
- 可哈希的东西,才可以作为字典的键和集合的元素
- hash(x) 有定义,即x可哈希
- hash(x) = x (如果x是整型常量)
- hash(x) = x.__ hash __()(如果x不是整型常量)
- object类有__ hash __()方法,返回值是个整数
- 列表、集合、字典的__ hash __成员函数都被设置成None,因此他们都不能成为集合的元素或者字典的键,因为无法计算
- 整数类型变量、小数、字符串、元组的哈希值,是根据自己的值算出来的
- 只要值相同,哈希值就相同
- 这个过程称为**“哈希映射”**
与集合、字典的关系
- 只有两个对象的哈希值不同,才能处在同一个集合里或作为同一个字典的不同元素的键
- 用哈希映射,实现集合的去重,和键值的去重
- 若两个对象哈希值相同,但是a==b不成立,那么也可以处在同一个集合里或作为同一个字典的不同元素的键
- 若dt是个字典,dt[x]计算过程如下:
- 根据hash(x)去找x应该在的槽的编号
- 如果该槽没有元素,则认为dt中没有键为x的元素
- 如果有元素,则在槽中找一个元素y==x
- 如果找到,则dt[x]=y的值(对应的键值)
- 如果没有找到,则dt[x]没定义
自定义类的对象,默认情况下哈希值是根据对象id进行计算。
因此,只要 a is b不成立,a和b的哈希值就不同
可以重载自定义类的__ hash __()方法,使得对象的哈希值和对象的值相关,而不是id相关
- 这样,只要对象的值相同,就不能处在一个集合里
- 也不能作为同一字典不同元素的键
class A:
def __init__(self,x):
self.x=x
a,b=A(5),A(5)
dt={a:20,A(5):30,b:40}
print(len(dt),dt[a],dt[b])
#>>3 20 40
print(dt[A(5)])
#>>KeyError: <__main__.A object at 0x000001EE07C3CC10>
自定义类是否可哈希的讨论
默认情况下,a==b等价于a.__ eq __ (b);
- 自定义类的默认 __ eq __函数是判断两个对象的地址是否相同
- 自定义类的默认 __ hash __函数是判断两个对象的地址计算哈希值
如果自定义类重载了__ eq __ (self,other)成员函数,
则其__ hash __成员函数自动设置为None,变为不可哈希
因此表明,哈希的操作,是进行了a==b的调用
因此,想要满足自行定义哈希操作,应该重载eq和hash两个函数
class A:
def __init__(self,x):
self.x=x
def __eq__(self,other):
if isinstance(other,A):#判断other是不是类A的对象
return self.x==other.x
elif isinstance(other,int):#如果other是整数
return self.x==other
else:
return False
def __hash__(self):
return hash(self.x)
a=A(3)
print(3==a)#>>True 重载过了
b=A(3)
d={A(5):10,A(3):20,a:30,b:20}
print(len(d))#>>2
print(d[a])#>>20
print(d[b])#>>20 保留最末尾那个
print(d[A(5)])#>>10