一、python中的类
使用 class
关键字创建类。类中有方法、属性。
1.1 __init__() 函数
类的内置 __init__()
函数。所有类都有一个名为 __init__() 的函数,它在启动类时执行。
使用 __init__() 函数将值赋给对象属性,或者在创建对象时需要执行的其他操作。
每次使用类创建新对象时,都会自动调用 __init__() 函数。
class Person: nation = "China" #属性nation def __init__(self, name, age): self.name = name #外部传入参数赋值给属性name self.age = age def get_nation(self): print(self.nation) #对象中的方法是属于该对象的函数p1 = Person("Bill", 63) #创建名为 Person 的类,使用 __init__() 函数为 name 和 age 赋值:print(p1.name)print(p1.age)print(p1.nation)p1.get_nation()
1.2 self 参数
self
参数是对类的当前实例的引用,用于访问属于该类的变量。
它不必被命名为 self
,您可以随意调用它,但它必须是类中任意函数的首个参数:
实例
使用单词 mysillyobject 和 abc 代替 self:
class Person: def __init__(mysillyobject, name, age): mysillyobject.name = name mysillyobject.age = age def myfunc(abc): print("Hello my name is " + abc.name)p1 = Person("Bill", 63)p1.myfunc()
1.3 修改对象属性
可以这样修改对象的属性:
p1.age = 40
1.4 删除对象属性
使用 del
关键字删除对象的属性:
del p1.age
1.5 删除对象
使用 del
关键字删除对象:
del p1
1.6 pass 语句
类定义不能为空,但若要写无内容的类 或类方法,使用 pass 语句来避免错误。
class Person: passclss Person: nation = "china" def say_hi(self): pass
二、继承: 子类继承父类
父类:
子类:在创建子类时将父类作为参数传入。
继承: 继承另一个类的所有方法和属性。
多继承:A类、B类是两个没有继承关系的类,C类即继承了A类又继承了B类。
print('---------------------多继承------------------------')class A: pass class B: pass class C(A, B): pass
单继承:C类继承了B类,B类继承了A类,C、B、A是层次继承的关系。
print('---------------------单继承------------------------')class Animal: pass class Animal1(Animal): pass
2.0 代码例子:
#父类class Person: def __init__(mysillyobject, firstname, lastname): mysillyobject.name = firstname mysillyobject.family = lastname def myfunc(abc): print("Hello my name is " + abc.name)#子类,继承class Student(Person): def __init__(self, fname, year, lname): super().__init__(fname, lname) # 调用父类的属性赋值方法 self.graduationyear = year #子类自己的属性 def welcome(self): #子类自己的方法 print("Welcome", self.name, self.family, "to the class of", self.graduationyear)x = Student("Elon", 2019, "Musk")x.welcome()
2.1 添加 __init__() 函数
要把 __init__()
函数添加到子类(而不是 pass
关键字)。
注释:每次使用类创建新对象时,都会自动调用 __init__() 函数。
为 Student 类添加 __init__() 函数:
class Student(Person): def __init__(self, fname, lname): # 添加属性等
当添加 __init__() 函数时,子类将不再继承父的 __init__() 函数。
注释:子的 __init__() 函数会覆盖对父的 __init__() 函数的继承。
如需保持父的 __init__() 函数的继承,请添加对父的 __init__() 函数的调用:
class Student(Person): def __init__(self, fname, lname): Person.__init__(self, fname, lname) #不建议如此使用
2.1.1 使用 super() 函数
Python 还有一个 super()
函数,它会使子类从其父继承所有方法和属性:
通过使用 super()
函数,您不必使用父元素的名称,它将自动从其父元素继承方法和属性。
class Student(Person): def __init__(self, fname, lname): super().__init__(fname, lname) #推荐方法
2.2 添加属性
把名为 graduationyear
的属性添加到 Student
类:
class Student(Person): def __init__(self, fname, lname): super().__init__(fname, lname) self.graduationyear = 2019
例子中,2019 年应该是一个变量,并在创建 student 对象时传递到 Student 类。为此,请在 __init__() 函数中添加另一个参数:添加 year
参数,并在创建对象时传递正确的年份:
class Student(Person): def __init__(self, fname, lname, year): super().__init__(fname, lname) self.graduationyear = yearx = Student("Elon", "Musk", 2019)
2.3 添加方法
把名为 welcome
的方法添加到 Student 类:
class Student(Person): def __init__(self, fname, lname, year): super().__init__(fname, lname) self.graduationyear = year def welcome(self): print("Welcome", self.firstname, self.lastname, "to the class of", self.graduationyear)
提示:如果在子类中添加一个与父类中的函数同名的方法,则将覆盖父方法的继承。
三、扩展: 覆盖(or重写) /重载 (override/ reload)
3.1 扩展
如果在开发中,子类的方法中包含父类的方法,父类原本的方法是子类方法的一部分,就可以采用扩展的方式。
扩展的方式步骤:
- 在子类中重写父类的方法
- 在需要的位置使用 super().父类方法 来调用父类方法的执行
- 代码其他的位置针对子类的需求,编写子类特有的代码实现
第2点.关于super:
- 在python中super是一个特殊的类
- super()就是使用super类创建出来的对象
- 最常使用的场景就是,在重写父类方法时,让super().调用在父类中封装的方法
#单继承, 子类对父类扩展:新增方法、重写方法、子类中使用super().调用父类方法class Animal(): def eat(self): print("吃") def run(self): print("跑") def drink(self): print("喝") def sleep(self): print("睡")class Dog(Animal): def bark(self): print("汪汪叫")class XiaoTianQuan(Dog): def fly(self): print("会飞") def bark(self): # 1. 针对子类特有的需求,编写代码 print("天籁之音") # 2. 使用super(). 调用原本在父类中封装的方法 super().bark() # 3. 增加其他子类的代码 print("OKOK")# 创建一个哮天犬对象xtq = XiaoTianQuan()xtq.bark()xtq.drink()xtq.sleep()
3.2 重载 overload
重载(Overload):指类中多个方法具有相同的名字,但参数类型不同(或返回类型?返回类型不一定严格相同,兼容即可?)的现象。重载的设定是为了方便用户操作,以相同的方法名实现特定的功能,同时匹配不同的参数类型以满足功能的扩展性和需求的多样性。重载并不局限于构造器方法,它可以适用于类中的任何方法。
重载可以解决用户输入多样性的问题。比如你在调用spark方法的时候,经常发现相同函数名后 跟着多种不同的输入参数类型。
注意:
- 不允许方法名和参数类型相同,但返回类型不同的情况出现,因为程序无法判断返回哪一个。
- 方法的形参类型相同但顺序不同也构成重载,如add(double a,int b)和add(int a,double b)。
//下面例子中,同一个类中 Rectangle()方法 定义了3个,但它们参数个数或类型均不同。//我们在实例中即可以输入double型、也可以输入string的参数到Rectangle()方法中,//解决了输入参数的兼容性问题。这就是reload。class Rectangle{ public double height; public double width; //定义无参方法0 public Rectangle() { } //定义方法1 public Rectangle(double height,double width) { this.height=height; //this指代当前对象,用于区分方法中的形参 this.weight=width; } //定义方法1 public Rectangle(String height,String width) { this.height=Double.valueOf(height); this.weight=Double.valueOf(width); } public void calcuArea() { System.out.println("面积为:"+height*width); }}public class Test{ public static void main(String[] args) { Rectangle rec=new Rectangle(1,2); //创建实例1-调用方法1,输入参数是double型 rec.calcuArea(); // Rectangle rec1=new Rectangle("1","2"); //创建实例2-调用方法2,输入是string类型 rec1.calcuArea(); }}
3.3 覆盖
覆盖(Override):指在继承中,父类的有些方法在子类中不适用,子类重新定义。
注意:
- 若子类中被“覆盖”方法的参数类型不同,返回类型不一致,这不是覆盖,而是重载。覆盖要求参数类型必须一样,且返回类型必须兼容。总之,子类对象得保证能够执行父类的一切。
- 不能降低覆盖方法的存取权限,如public变成private。
- 若不希望父类的某个方法被子类覆盖,可以用final修饰该方法。甚至可以扩展到将类用final修饰,则其中所有的方法均不可覆盖,但不影响成员变量的赋值。
子类如何重写父类的方法?
前提:
规则一:重写方法不能比被重写方法限制有更严格的访问级别。
(但是可以更广泛,比如父类方法是包访问权限,子类的重写方法是public访问权限。)
规则二:参数列表必须与被重写方法的相同。
(需要注意的是如果子类方法的参数和父类对应的方法不一样,那就不是重写,而是重载)
规则三:返回类型必须与被重写方法的返回类型相同。
规则四:重写方法不能抛出新的异常或者比被重写方法声明的检查异常更广的检查异常。但是可以抛出更少,更有限或者不抛出异常。
规则五:不能重写被标识为final的方法。
规则六:如果一个方法不能被继承,则不能重写它。比如父类的私有方法就不能被重写。
如果子类能够继承父类的某个方法, 那么子类就能够重写这个方法。
# 如果子类中重写了父类方法# 在使用子类对象调用方法时,会调用子类中重写的方法class XiaoTianQuan(Dog): def fly(self): print("会飞") def bark(self): print("OKOK")class TuDog(Dog): def fly(self): pass def bark(self): print("小土狗")# 创建对象1xtq = XiaoTianQuan()xtq.bark()xtq.drink()xtq.sleep()# 创建对象2tg = TuDog()tg.bark()tg.fly()
四、多态
4.1 多态
多态就是“具有多种形态”,它指的是:即便不知道一个变量所引用的对象到底是什么类型,仍然可以通过这个变量的调用方法,在运行过程中根据变量所引用对象的类型,动态决定调用哪个对象中的方法。
当子类和父类存在相同的方法的时候,子类的方法会覆盖父类的方法,这样代码在运行时总会调用子类的方法,这就是多态。
判断一个实例是不是某个对象,可以使用isinstance()函数,是则输出True,反之输出False。
print('---------------------多态------------------------')class Animal: def say(self): print('Animal')class Dog(Animal): def say(self): print('Dog')class Cat(Animal): def say(self): print('Cat')animal = Animal()animal.say()dog = Dog()dog.say()cat = Cat()cat.say()print('-------------------------isinstance()----------------------')print(isinstance(dog, Dog))print(isinstance(dog, Cat))print(isinstance(dog, Animal))
五、封装
5.1封装
封装: 将数据(属性)和行为(方法)包装到类对象中。在方法内部对属性进行操作,在类对象的外部调用方法。 这样,无需关心方法内部的具体实现细节,从而隔离了复杂度。提高了程序的安全性。
在Python中没有专门的修饰符用于属性的私有,如果该属性不希望在类对象外部被访问,前边使用两个“-”。
六、object类
6.1object类
object类是所有类的父类,因此所有类都有objec类的属性和方法。
内置函数dir()可以查看指定对象所有属性。 objec有一个_str_()方法,用于返回一个对于“对象的描述”,对于内置函数str()经常用于print()方法,帮我们查看对象的信息,所以我们经常会对_str_()进行重写。
print('-----------------------------------------------------')class A: passclass B: passclass D(A): passclass C(A, B): def __init__(self, name, age): self.name = name self.age = ageprint('----------------------特殊属性-----------------------')x = C('ss', 10)print('----------------__dict__----------------')print(x.__dict__) # 实例对象的属性字典print(C.__dict__) # 类对象的属性字典print('----------------------------------------')print(x.__class__) # 输出了对象所属的类print(C.__bases__) # C类的父类类型的元组print(C.__base__) # C类的父类的第一个类print(C.__mro__) #查看类的层次结构print(A.__subclasses__()) # 子类的列表print('----------------------特殊方法-----------------------')print('--------------__add__整数相加操作----------------')a = 20b = 10c = a+bd = a.__add__(b)print(c)print(d)print('--------------__add__----------------')