1.类的定义
1.1 类的定义和使用:
- 类定义
class <类名>
<语句组>
- 类对象及其使用
执行一个类定义将创建一个类对象 :
- 属性访问(成员变量)
- 实例化
- 实例对象 : 初始化和使用
创建一个类的实例对象:实例对象名 = 类名(属性),将自动调用 类中的 init() 方法完成初始化
class C:
""":arg
说明:
Python 中提供了一个内置函数 isinstance(),专门用来检查类和对象的关系。
isinstance(obj, cls) : 检测对象obj是否为类cls的实例,是则返回True,否则返回False;
也可以检测 任何对象 和 任何类型的关系 eg: isinstance(12, int) : 判断一个变量是否为int型
"""
def __init__(self, m=1, n=1):
self._m = m # _m 表示 该变量是私有的,只能在类中使用
self._n = n # 成员变量,又叫 数据属性
self.__x = 10
# 定义方法函数,self 表示 本实例对象,self 是约定俗称的写法,可以写其他的my等
def m(self, a, b):
return self._m * a + self._n * b
pass
1.2 静态方法和类方法
- 静态方法
- 定义:def 行前 加修饰符 @staticmethod
- 静态方法是 普通函数,只是由于某种原因需要定义在类里面。
- 静态方法的参数可以根据需要定义,不需要特殊的 self 参数
- 调用:通过 类名.静态方法名() 或者 实例对象.静态方法名()
eg : Rational1 类 中的 _gcd() 方法即是静态方法。
- 定义一个计数器,返回 该类中实例化多少个对象
get_count() 即是一个 类方法:
- 定义形式为 def 前, 加修饰符 @classmethod
- 这种方法必须有一个表示其调用类的参数,习惯用 cls 作为参数名,还可以有任意多个其它属性
- 类方法 也是 类对象的 属性, 可以 以 属性访问形式调用
- 在类方法执行时,调用它的类将自动约束到方方的cls参数,可以通过这个参数访问该类的其他属性
class Countable:
counter = 0
def __init__(self):
Countable.counter += 1
@classmethod # 类方法
def get_count(cls):
return Countable.counter
pass
class Rational1:
""":arg
1.考虑输入时分母不为0,输入数字有正负之分,我们统一规定输入的分母都为正,分子为正则为正分数,分子为负则为负分数
2.对结果进行约分时,我们需要找到醉倒公约数,该方法的定义是局部使用的,可以说是可以分离出来的,我们定义静态方法
方法前加入: @staticmethod 方法里去掉 self 静态方法的调用: 类名.静态方法() 或者 实例对象.静态方法()
本质上说 静态方法是类里面定义的普通函数,但也是该类的局部函数。
辗转相除法求2个数的最大公约数:
例:求 80 和 75 的最大公约数:
80 / 75 = 1......5 75 / 5 = 15......0 5 / 0 则最大公约数为 5
30 % 5 = 0 5 % 0 """
@staticmethod
def _gcd(m, n): # _ : 方法名前单个下划线:当做内部使用的名字,不能在这个类外使用
if m < n:
m, n = n, m # 保证 m > n while n != 0:
m, n = n, m % n
return m # 当 余数为0时, m 的值即为这m/n的最大公约数
def __init__(self, num, den=1):
if not isinstance(num, int) or not isinstance(den, int):
raise TypeError
if den == 0:
raise ZeroDivisionError
sign = 1 # 分母正负的标志
if num < 0:
num, sign = -num, -sign
if den < 0:
den, sign = -den, -sign
g = Rational1._gcd(num, den)
# print(f'g:{g}')
self._num = sign * (num // g)
self._den = den // g
def print(self):
print(str(self._num) + '/' + str(self._den))
# 成员变量是私有的,只能在本类中使用,不能 对象.成员变量名 调用,如果要使用成员变量可以定义实例方法
def num(self):
return self._num
def den(self):
return self._den
# 有理数的运算,Python中的特殊方法名:都以2个下划线开始,并以2个下划线结束。
# +
def __add__(self, another):
den = self._den * another.den() # 通分
num = (self._num * another.den() + self._den * another.num())
return Rational1(num, den)
# -
def __sub__(self, another):
num = self.num * another.den() - self._den * another.num()
den = self._den * another.den()
return Rational1(num, den)
# *
def __mul__(self, another):
return Rational1(self._num * another.num(), self._den * another.den())
# 整除 : // def __floordiv__(self, another):
if another.num() == 0:
raise ZeroDivisionError
return Rational1(self._num * another.den(), self._den * another.den())
# 比较有理数对象相等或不等
# == : __eq__
def __eq__(self, another):
return self._num * another.den() == self._den * another.num()
# < : __lt__
def __lt__(self, another):
return self._num * another.den() < self._den * another.num()
# > : __gt__
def __gt__(self, another):
return self._num * another.den() > self._den * another.num()
# != : __ne__
def __ne__(self, another):
return self._num * another.den() != self._den * another.num()
# <= : __le__
def __le__(self, another):
return self._num * another.den() <= self._den * another.num()
# >= : __ge__
def __ge__(self, another):
return self._num * another.den() >= self._den * another.num()
# 定义把类的对象 转化成 字符串: __str__ , 内置 str 将会调用它
def __str__(self):
return str(self._num) + "/" + str(self._den)
def print(self):
print(type(self._num), type(self._den))
print(self._num, "/", self._den)
pass
2.继承
2.1 继承的定义:
class <类名>(BaseClass, ...):
<语句组>
通过继承定义出的新类称为 派生类(或者 字类),被继承的已有类,称为 基类(或者 父类),一个新定义的 派生类 可以继承多个 基类。
(BaseClass, …), 括号里面的"参数"是指定的基类,可以有一个或多个。
class MyStr(str):
''':arg
MyStr 类 继承了 str 类,派生类里面没有任何操作
'''
pass
# issubclass(cls1, cls2) : 判断 cls2 是否为 cls1 的基类(父类),若是,则返回 True,否则返回 Falses = MyStr(1234) # 创建 MyStr 类型的对象
print(issubclass(MyStr, str))
print(isinstance(s, MyStr))
print(isinstance(s, str)) # 派生类对象也是基类的对象
2.2 继承常见的形式
派生类经常需要重新定义 init 函数,完成该类实例的初始化。
常见情况是要求 派生类的对象可以作为基类的对象,用在要求基类对象的环境中。
- 常见形式:
class DerivedClass(BaseClass):
def __init__(self, ...):
BaseClass.__init__(sefl, ...) # 调用 基类(父类) 的 __init__ 函数
....... # 初始化的其他操作
# 派生类的其他语句和函数定义
以上语句,初始化时调用 基类 的 init 方法,对基类对象的所有 数据属性(成员变量)进行初始化。
- 方法查找
派生类的实例对象调用一个方法时,派生类和父类都有该方法时或者仅有一方有该方法,该怎样寻找哪?
如果从一个派生类的实例对象出发去调用方法,Python 解释器需要确定应该调用按个函数。
查找过程是 从该实例对象所属的类开始查找,没有找到则查找该实例对象对应类的基类中查找,如果最终都没有
查找到函数属性,那就是属性无定义。
class B:
def f(self):
self.g()
def g(self):
print('B.g called.') # 基类(父类)中定义有 g() 方法
class C(B): # 派生类 C 继承 基类 B def g(self): # 派生类中 也定义了 g() print('C.g called.')
x = B() # 创建 B 的实例对象 xx.f() # 调用 f() 函数
y = C() # y 是 类C 的实例对象
y.f() # 此时类B中的f() 里的 g() 是调用 类B 还是 类C 里的哪?
- 静态约束 和 动态约束
执行情况:y 是 类C 的实例化对象,Python解释器先从 类C 中查找f(), 一遍之后没有找到,则去基类
B 中去查找,查找到之后,则执行 基类的 f() 方法, 此时出现问题:
基类 f() 里有一个 g(),我们该执行 类B 还是 类C 里的 g() 哪?
- 因为 y 是 类C 的实例对象,因为 派生类中没有 f() ,则调用 基类中的f()
- 基类中的self则指向是实例对象 y, 因此 self.g() 即是调用 y.g(),故调用 C类中的g() 方法。
以上即是 动态约束(大多数面向对象的语言都是这样的)。
静态约束(静态绑定):上例中 self.g() 直接调用 类f 里的g()方法,则为静态约束。
- 标准函数 super()
Python 中提供了一个内置函数 super(), 就是要求从这个类的 直接基类开始做属性检索,而不是从这个类本身开始查找。
采用 Super函数 而不是直接写基类名字,产生的查找更加的灵活。super() 函数的使用分为2种:
- 不带参数的调用形式
super().m(…) 这种形式只能出现在方法函数的定义里,在函数调用时,当前实例将被作为被调用函数的self实参。 - 带参数的调用
super(C, obj).m(…) 这种写法要求从指定的类C的基类开始查找函数属性m, 调用里出现的 obj 必须是类C的一个实例。Python解释器找到函数m,将用 obj 作为该函数
的self实参。这种写法可以出现在程序的任何地方,并不要求一定出现在类的方法函数里。
class C1:
def __init__(self, x, y):
self.x = x
self.y = y
def m1(self):
print(self.x, self.y)
class C2(C1):
def m1(self):
super().m1() # 从基类查找 函数 m() print("这是派生类")
c = C2(10, 20) # class C2 没有定义 init方法,初始化时直接调用 基类 C1 的init方法
c.m1() # 调用 class C2 的 m1()
[完]