郭炜实用Python程序设计慕课:chapter13面向对象程序设计

<学习记录,自己参考>

一、类和对象的概念

1、类和对象:类的实例称为“对象”,类代表一种事物的共同特点,对象就是一个具体的事物个体

2、生成对象的方法:类名(参数1,参数2)

3、类的写法:

class 类名:
    def _init_(self, 参数1, 参数2, ...):  # 构造函数,每个类必须有
        ...
    def 成员函数1(self, 参数1, 参数2, ...):
        ...
    def 成员函数2(self, 参数1, 参数2, ...):
        ...

# 矩阵类示例:
class rectangle:
    def _init_(self, w, h):
        self.w, self.h = w, h
    def area(self):
        return aslf.w * self.h
    def perimeter(self):
        return 2 * (self.w + self.h) 

def main():
    w, h = map(int, input().split())  # 输入2,3
    rect = reactangle(w,h)  # 生成一个reactangle对象
    print(rect.area(), rect.perimeter())  # 6,10
    rect.w, rect.h = 10, 20
    print(rect.area(), rect.perimeter())
    rect2 = rectangle(2, 3)
    print(rect.area(), rect.perimeter())

main()

self不对应实参,写的时候不用写

4、类的作用:将数据和操作数据的函数捆绑在一起,便于当作一个整体使用

5、Python中的类:

       类型名即是类名,如:float,str,list,dict...

        小数、复数、字符串、元祖、列表、集合、字典等组合数据类型的常量,都是对象,函数也是对象,但整数型常量不是对象

        各种库都是由类组成,如:turtle,jieba...

        也可以自定义类

二、对象的比较

  • Python中所有的类,包括自定义的类,都有__eq__方法
  • x == y的值,就是x.__eq__(y)的值(x,当做一个对象看待,__eq__()这个对象所属类的成员函数,x是个整型常量这个就不对了);如果x.__eq__(y)没定义,那么就是y.__eq__(x)的值。如果x.__eq__(y)和y.__eq__(x)都没定义,则x == y也没定义(x,y都是整数常量时不适用)
print(24.5.__eq__(24.5))  #True
  •  a != b等价于a.__ne__(b),或b.__ne__(a)(若a.__ne__(b)没定义)
  • 默认情况下a.__ne__(b)等价于not a.__eq__(b)
  • a < b  等价于 a.__lt__(b)         a > b 等价于a.__gt__(b)
  • a <= b 等价于a.__le__(b)        a >= b 等价于a.__ge__(b)

自定义对象的比较

  • 默认情况下,一个自定义类的__eq__方法,功能是判断两个对象的id是否相同( a==b和a is b含义一样,都是“a和b是否指向相同的地方”)
  • 默认情况下,自定义类的对象不能比较大小,因为其__lt__、__gt__、__le__、__ge__方法都被设置成了None
# 对象比较大小程序实例
class point:
    def __init__(self, x, y=0):
        self.x, self.y = x, y
    def __eq__(self, other):
        return self.x == other.x and self.y == other.y
    def __lt__(self, other):  # 使两个point对象可以用<进行比较
        if self.x == other.x:
            return self.y < other.y
        else:
            return self.x < other.x
a, b = point(1,2), point(1,2)
# a和b分别是两个对象,本来调用默认的__eq__会不等,因为不指向同一个位置
# 但是改写后的__eq__判断内容是否相等
print(a == b)  # True
print(a != b)  # False
print(a < point(0, 1))  # 等价于 a.__lt__(point(0,1))
print(a < point(1, 3))  # True
lst = [a, point(-2, 3), point(7, 8), point(5, 9), point(5, 0)]
lst.sort()
for p in lst:
    print(p.x, p.y, end=",")
# 自定义重写__str__方法,可以将对象转字符串
class point:
    def __init__(self, x, y):
        self.x, self.y = x, y
    def __str__(self):
        return ("(%d,%d)" % (self.x, self.y))
print(point(3, 5))
print(str(point(2, 4)))

三、继承和派生

比如,要写小学生类、中学生类、大学生类...

所有学生都有共同特点,每种学生又有各自特点。可以选择使用继承(派生)

 B类“继承”A类的好处:B类自动拥有了A类的全部属性和方法

class 类名(基类名):
    ...

object类

所有类都是object类的派生类,因而具有object类的各种属性和方法

有的类__lt__,__gt__等方法被设置成None,于是对象不可比较大小

四、静态属性和静态方法

  • 静态属性被所有对象所共享,一共只有一份
  • 静态方法不是作用在将具体的某个对象上的,不能访问非静态属性
  • 静态属性和静态方法的存在目的,就是为了少写全局变量和全局函数
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("Jack", 0)
e2 = employee("Tom", 0)
e1.pay(100)
e2.pay(200)
employee.printTotalSalary() #>>300 用类名调用静态方法
# 静态方法不能作用在具体的对象上,这个虽然写了e1..printTotalSalary()打印出结果了,
# 但是printTotalSalary()和e1其实没有关系,只跟e1e2所属类有关,所以这个写法和上面的其实一样
e1.printTotalSalary() #>>300
e2.printTotalSalary() #>>300
print(employee.totalSalary) #>>300

静态属性和静态方法,用来替代全局变量和全局函数用的,使得全局变量和全局函数被绑到类里,让它和类的关系一目了然。

五、对象作为集合元素或字典的键

“可哈希”:

  • 可哈希的东西,才可以作为字典的键和集合的元素
  • hash(x)有定义,能算出一个值来,即为x可哈希
  • hash(x) = x (如果x是整型常量)   hash(x) = x.__hash__() (如果x不是整型常量,x就是一个对象)
  • object类有__hash__()方法,返回值是个整数

列表、集合、字典的__hash__成员函数都被设置成None,因此它们都不能成为集合的元素,或者字典的键,因为无法计算哈希值。

 整数类型变量、小数、字符串、元组的哈希值,是根据它们的值算出来的,只要值相同,哈希值就相同。

x = 23.1
print(x.__hash__(), 23.1.__hash__())  #230584300921372695 230584300921372695
x = 23
print(x.__hash__(), hash(23))  # 23 23
x = (1, 2)
print(x.__hash__(), (1, 2).__hash__(), hash(x))  # 3713081631934410656 3713081631934410656 3713081631934410656
x = "ok"
print(x.__hash__(), "ok".__hash__())  # -423760875654480603 -423760875654480603

哈希值和字典、集合的关系

 若dt是个字典,dt[x]计算过程如下:

  1. 根据hash(x)去找x应该在的槽的编号
  2. 如果该槽没有元素,则认为dt中没有键为x的元素
  3. 如果该槽中有元素,则视图在槽中找一个元素y,是得y的键等于x。如果找到,则dt[x]即为y的值,如果找不到,则dt[x]没定义,即认为dt中不存在键为x的元素。

class A:
    def __init__(self, x):
        self.x = x
a, b = A(5), A(5)  # a,b的值相同,但两个A(5)不是同一个,因此a和b的id不同
dt = {a:20, A(5):30, b:40}  # 三个元素的键id不同,因此在不同的槽里
print(len(dt), dt[a], dt[b]) #>>3 20 40 dt[b]求键为b的元素的值
print(dt[A(5)])  # 找一个键为A(5)的元素的值,找不到,因为A(5)是一个新的对象,不是dt里面一个元素的键

自定义类的对象是否可哈希

# 自定义类只重写__hash__,不重写__eq__

class A:
    def __init__(self, x):
        self.x = x
    def __hash__(self):  # 重写__hash__
        return hash(self.x)
c = A(1)
dt = {A(1):2, A(1):3, c:4}
# 三个元素的键哈希值相同,但id不同,它们在同一个槽里
print(len(dt))  # 3
for a in dt.items():
    print(a[0].x, a[1], end=",")  # 1 2,1 3,1 4,
print(dt[c])  # 4
print(dt[A(1)])  # runtime error
# 因不存在元素的键x,满足 x == A(1) (特指最后一行的A(1)) 
# 默认的==号会调用默认的__eq__,默认的__eq__判断相等是看id是否相同,是否指向同一个地方



# 自定义类同时重写__hash__和__eq__

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 self.x

a = A(3)
print(3 == a)  # True 等价于a.__eq__(3)调用重写的函数
b = A(3)
d = {A(5):10, a:32, A(3):30}
print(len(d),d[a],d[b],d[3])  # 2(因为a和A(3)被认为是重复的了) 30 30 30

猜你喜欢

转载自blog.csdn.net/m0_46303430/article/details/125884450