面向对象初步
1. 注意: Python支持 (1)面向过程 (2)面向对象 (3) 函数式编程等多种编程范式 2. 面向对象编程的思想主要是针对大型软件设计而来的. 面向对象编程使得程序的扩展性更强,可读性更好,使得编程可以像搭积木一样简单. 3. 面向对象编程将数据和操作数据相关的方法封装到对象中,组织到吗和数据的方式更加接尽人的思维,从而大大提高了编程的效率. 4. Python完全采用了面向对象的思想,是真正面向对象的编程语言,完全支持面向对象的基本功能,例如: (1)继承(2)多态(3)封装等. Python中一切都是对象,我们前面学习的(1)数据类型(2)函数都是对象.
面向对象和面向过程区别
1. 面向过程编程更加关注的是"程序的逻辑流程",是一种"执行者"思维,适合编写小规模的程序. (1) 面向过程思考方式 => 完成这件事情的步骤是(1.... 2... 3...) 2. 面向对象更加关注的是"软件中对象之间的关系",是一种"设计者"思维,适合编写大规模的程序. (1) 面向对象思考方式 => 完成这件事情需要由哪些对象来配合(需要调用哪些类) 3. 解决简单问题使用面向过程; 解决复杂问题: 宏观上是以哦那个面向对象把握,微观处理上仍然是面向过程. (一个优秀的软件设计师必然从程序员干起,底层的实现细节肯定也是十分了解)
对象的进化
类的定义
1. Python中,"一切皆对象". 类也成为"类对象",类的实例也称为"实例对象". 2. 定义类的语法 class 类名: 类体 3. 定义类的要点: (1) 类名必须符合"标识符"的规则,规定首字母大写,多个单词采用"驼峰原则". (2) 类体中我们可以定义属性和方法 (3) 属性(变量)用来描述数据,方法(即函数)用来描述这些数据相关的操作.
# code01_一个典型类的定义.py class Student: def __init__(self, name: str, score: int) -> None: # 注意: self参数必须位于第一位 self.name = name self.score = score def say_score(self)->None: # 注意: self参数必须位于第一位 print("{0}的分数为:{1}".format(self.name, self.score)) def main(): # 解释器会默认把s1的地址传递给参数self s1 = Student("rowry", 98) # 类名() 调用构造方法 s1.say_score() # 调用对象的方法 if __name__ == "__main__": main()
__init__
构造方法和__new__
方法
__init__()
的要点如下:
1. 名称固定,必须为 __init__(). 2. 第一个参数固定(类中的方法第一参数都必须为self),必须为self. (1)self指的就是刚刚创建好的实例对象(的内存地址). (2) Python中的self相当于C++中的self指针, Java和C#中的this关键字. (3) Python中self必须为构造参数的第一个参数,名字可以任意修改,但是一般遵守惯例,都叫做self. 3. 构造函数通常用来初始化实例化对象的实例属性(变量) => 给属性赋初值 def __init__(self,name,score): self.name = name self.score = score 4. 通过"类名(参数)"来调用构造函数(__init__). 解释器创建创建好对象后将对象的内存地址返回给相应的变量. (1) 比如: s1 = Student("rowry",98) 5. __init__(self) 用于初始化创建好的对象. 初始化的意思是 "给实例属性赋值". 6. __new__(self) 用于创建对象. 一般是不需要重定义该方法的,知道有这个方法即可. 7. 如果我们不定义__init__(self)方法,系统会提供一个默认的无参__init__(self),如果我们定义了带参数的__init__(self),系统不创建默认的__init__(self).
实例属性和实例方法
实例属性
实例属性(变量)是从属于实例对象的属性,也称为"实例变量". 他的使用有如下几个要点: 1. 实例属性一般在 __init__() 中通过如下代码定义: self.实例属性名 = 初始值 2. 在本例的其他实例方法中,也是通过self进行访问: self.实例属性名 3. 创建实例对象后,通过实例对象访问: obj01 = 类名() # 创建对象,调用__init__() 初始化属性 obj01.实例属性名 = 值 # 可以给已有属性赋值,也可以新加属性
实例方法
实例方法只是一个指向,并不是重新在定义一个方法没有必要
实例方法是从属于实例对象的方法. 1. 实例方法定义 def 方法名(self,[,形参列表]): 函数体 2. 实例方法调用: 对象.方法名([实参列表]) 3. 要点 (1) 定义实例方法时,第一个参数必须为self,和前面一样,self指当前的实例对象. (2) 调用实例方法时,不需要也不能给self传参. self由解释器自动传参. 4. 函数和方法的区别 (1) 都是用来完成一个功能的语句块,本质一样. (2) 方法调用时,通过对象来调用. 方法从属于特定实例对象,普通函数没有这个特点. (3) 直观上看,方法定义时需要传递self,函数不需要 5. 实例对象的实例方法调用本质 (1) 对象.实例方法() <=> 类名.实例方法(对象) (2) 一般我们使用前者进行调用,然后解释器会将前者翻译为后者 6. 其他操作 (1) dir(对象)可以获得对象的所有属性,方法 (2) 对象.__dict__ 对象的属性字典, 获得自定义的对象属性变量字典 (3) pass 空语句 (4) isinstance(对象,类[型]) 判断"对象"是不是"指定类型"
# code02_实例属性和实例方法.py class Student: def __init__(self, name: str, score: int) -> None: # 注意: self参数必须位于第一位 self.name = name self.score = score def say_score(self) -> None: # 注意: self参数必须位于第一位 self.xxx = "xxx" # 动态新增的实例属性 print("{0}的分数为:{1} test新增实例属性{2}".format(self.name, self.score, self.xxx)) def main(): # 解释器会默认把s1的地址传递给参数self s1 = Student("rowry", 98) # 类名() 调用构造方法 s1.say_score() # 调用对象的方法 # Python对象可以动态添加属性(变量) s1.age = 18 s1.salary = 3000 s1.abs = abs # 其实是一样的,只是新增加了一个实例属性变量,这个变量指向abs函数 print(f"{s1.name}的年龄:{s1.age},工资:{s1.salary}") # 重新用该类创建创建的对象是没有s1新增加的属性的 s2 = Student("张三", 60) # 是没有 age 和 salary 的 # 实例方法的本质 # 对象.方法名() <=> 类名.方法名(对象) s2.say_score() Student.say_score(s2) # dir(对象) 查看对象的所有属性和方法 print(dir(s1)) print(dir(s2)) # 对象.__dict__ 对象的属性字典(自定义的属性) print(s1.__dict__) print(s2.__dict__) # isinstance(对象,类型) 判断给对象是不是该类型的实例 print(isinstance(s1, Student)) if __name__ == "__main__": main()
类对象,类属性,类方法,静态方法
类对象
1. 我们签名将类定义格式中, "class 类名: ". 实际上,当解释器执行class语句时,就会创建一个类对象 (类本身就是一个对象, 是 type 类型的对象)
# code03_类对象的生成.py class Student: pass def main(): print(type(Student)) # 所有自定义类都是type类型 print(id(Student)) # 已经有Student这个对象了,执行class时候就自动创建Student这个类对象 s1 = Student() # 现在相当于调用 __init__ 和 __new__ # 下面这种方式应该更加帮助理解类对象 stu = Student s2 = stu() if __name__ == "__main__": main()
类属性
1. 类属性是从属于"类对象"的属性,也称为"类变量". 由于类属性从归属于类对象,可以被所有实例对象共享. 2. 类属性的定义方式: class 类名: 类变量名 = 初始值 3. 在类中或者类的外部,可以通过"类名.类变量名"来进行读写
# code04_类属性.py class Student: company = "ByteDance" # 类属性 count = 0 # 类属性 def __init__(self, name, score): self.name = name # 实例属性 self.score = score # 实例属性 Student.count += 1 # 使用类属性 def say_score(self): # 实例方法 print("我的公司是:", Student.company) print(f"{self.name}的分数是:{self.score}") def main(): s1 = Student("张三", 80) s1.say_score() s2 = Student("李四", 85) s2.say_score() print("一共创建{0}个Student对象".format(Student.count)) if __name__ == "__main__": main()
类方法
1. 类方法是从属于"类对象"的方法. 类方法通过装饰器@classmethod来定义,格式如下: @classmethod def 类方法名(cls,[,形参列表]): 函数体 2. 类方法的要点: (1) @classmethod必须位于方法上面一行 (2) 第一个cls参数必须有; cls指的是"类对象"本身,与self用法一致 (可以改名,但是一般不改) (3) 调用类方法格式: "类名.类方法名(参数列表)". 参数列表中,不需要也不能给cls传值. (4) 类方法中访问实例属性和实例方法会导致错误(类创建了,对象还没有创建,当然不能使用实例属性和实例方法) (5) 子类继承父类方法是,传入cls是子类对象,而非父类对象.
# code05_类方法.py class Student: company = "ByteDance" @classmethod def print_company(cls): # 类方法第一个参数必须为cls print(cls.company) def main(): Student.print_company() # 直接运行类方法即可(class 已经创建了这个对象了) if __name__ == "__main__": main()
静态方法
1. Python中允许定义与"类对象"无关的方法,称为"静态方法". 2. "静态方法"和模块中定义普通函数没有区别,只不过"静态方法"放到了"类的名字空间里面",需要通过"类调用". 3. 静态放啊通过装饰器@staticmethod来定义,格式如下: @staticmethod def 静态方法名([形参列表]): 函数体 4. 静态方法要点: (1) @staticmethod必须位于方法上面一行 (2) 调用静态方法格式: "类名.静态方法名(参数列表)" (3) 静态方法中访问实例属性和实例方法会导致错误(需要传递self,但是还没有self呢)
# code06_静态方法.py class Student: company = "ByteDance" @staticmethod def add(a, b) -> None: # 静态方法(一般是不操作类属性的) print("{0}+{1}={2}".format(a, b, (a + b))) def main(): Student.add(10, 20) # 调用类的静态方法 if __name__ == "__main__": main()
内存分析实例对象和类对象创建过程(重要)
__del__
方法(析构函数)和垃圾回收机制
1. 在创建对象时,系统会自动调用__init__(),在对象被清理时,系统也会自动调用一个__del__(),这个方法就是析构函数. 2. Python实现自动的垃圾回收,当对象没有被引用时(引用计数为0),由垃圾回收器调用__del__(). 3. 我们也可以通过del关键字,手动调用__del__(). 4. 系统会自动提供__del__(),一般不需要自定义析构方法.
# code07_析构方法.py class Person: def __del__(self): print("销毁对象:", self) def main(): p1 = Person() p2 = Person() del p2 # 手动触发__del__() print("程序结束") # 程序结束收p1也会被自动回收 if __name__ == "__main__": main()
__call__
方法和可调用对象
对象() 实际上调用的是对象的 call()
定义了 call() 的对象,称为"可调用对象",即该对象可以像函数一样被调用. (函数本身也是一个对象)
# code08_可调用对象.py class SalaryAccount: def __call__(self, salary): print("算工资啦...") yearSalary = salary * 12 return f"年薪为:{yearSalary}" def main(): s = SalaryAccount() print(s(3000)) # 对象() <=> 函数() if __name__ == "__main__": main()
方法没有重载
Python是没有方法重载的,如果出现同名函数,只保留最后一个(相当于不断的重新给变量赋值)
1. 在其他语言中,可以定义多个重名的方法,只要保证方法签名唯一即可. (1) 方法签名包含3个部分 (a)方法名 (b)参数数量 (c)参数类型 2. Python中,方法的参数没有声明类型(调用时确定参数的类型),参数的数量也可以由可变参数控制.因此,Python中式没有方法的重载的. 定义一个方法即可有多种调用方式,相当于实现了其他语言中的方法重载. 3. 如果我们在类体中定义了多个重名的方法,只有最后一个方法有效.
方法的动态性
可以动态的修改 类方法,实例方法
本质就是方法也是一个对象,修改其实想到与给 类属性,对象实例属性 重新赋值一个对象引用而已
注意: 理解一下方法本质也是属性,只不过通过()执行 call() 而已
私有属性和素有方法(实现封装)
1. Python对于类的成员没有严格的访问控制限制,这与其他面向对象语言有区别. 2. 关于私有属性和私有方法,有如下要点: (1) 通常我们约定,两个下划线开头的属性是私有的(private),其他为公共的(public). (2) 类内部可以直接访问私有属性和私有方法 (3) 类外部不能直接访问私有属性和私有方法,但是可以间接以 _类名__私有属性 这样进行私有属性的访问(Python解释器的私有属性做的一些小变通)
# code09_私有属性和私有方法.py class Employee: __count = 100 # 类变量也是可以私有的 def __init__(self, name, age): self.__name = name # 定义私有属性 self.__age = age def __work(self): # 私有方法 pass def main(): e = Employee("张三", 18) # 私有属性和私有方法的存储名字 print(e.__dict__) # {‘_Employee__name‘: ‘张三‘, ‘_Employee__age‘: 18} print(dir(e)) # ‘_Employee__age‘, ‘_Employee__name‘, ‘_Employee__work‘ if __name__ == "__main__": main()
@propety装饰器
@property广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查.
1. @property 可以讲一个方法的调用方式变成"属性调用". 2. 对于下面的第一个程序,要修改score,如果使用直接调用属性修改 s.score = 220 首先220是不合法的分数,其次是直接这么修改虽然方便但是非常不安全 3. 使用getter和setter虽然可以安全的修改score,但是不如直接 对象.属性=值 这么修改来的方便,所以 => 既能参数检查,又能类似属性这样简单的方式访问类的变量 <= Python内置的 @property装饰器就是负责把一个方法变成属性调用的. 4. 对getter()上加上@property, 然后会生成 @xxx.setter 这个装饰器
# code10_property装饰器01.py class Student: def __init__(self, name: str, score: int): self.name = name self.__score = score # 现在设置一个 setter() 和 getter() 保证数据安全 def get_score(self): return self.__score def set_score(self, value: int): if not isinstance(value, int): raise ValueError("score must be an integer!") if value < 0 or value > 100: raise ValueError("score must between 0~100!") self.__score = value def main(): s = Student("张三", 60) # s.set_score(-20) s.set_score(20) print(s.get_score()) if __name__ == "__main__": main()
# code10_property装饰器02.py class Student: def __init__(self, name: str, score: int): self.name = name self.__score = score @property def score(self): return self.__score @score.setter def score(self, value: int): if not isinstance(value, int): raise ValueError("score must be an integer!") if value < 0 or value > 100: raise ValueError("score must between 0~100!") self.__score = value def main(): s = Student("张三", 60) # 把对属性的一个赋值操作(应该使用函数进行判断的) @property简化为直接赋值 # s.score = -20 s.score = 20 print(s.score) if __name__ == "__main__": main()
属性和方法命名总结
1. _xxx : 保护成员,不能用 from module import * 导入, 只有类对象和子类对象才能访问这些成员. 2. __xxx__ : 系统定义的特殊成员 3. __xxx : 类中的私有成员,只有类对象自己能访问,子类对象也不能访问. (但是私有成员在类外部可以通过 "对象名._类名__xxx"这种特殊方式访问. Python不存在严格意义的私有成员)
类编码风格
1. 类名首字母大写,多个单词之间采用驼峰原则. 2. 实例名,模块名采用小写,多个单词之间采用下划线隔开 3. 每个类,应紧跟"文档字符串",说明这个类的作用 4. 可以使用空行组织代码,但不能滥用. 在类中,使用一个空行隔开方法; 模块中,使用两个空行而开多个类.
实操作业
相关推荐
jessiejava 2020-10-10
Wyt00 2020-08-01
dailinqing 2020-08-01
adamlovejw 2020-06-11
HongKongPython 2020-06-09
Wonder的学习 2020-06-08
Freeman00 2020-06-07
钟鼎 2020-06-05
jameszgw 2020-06-03
RocNg 2020-06-01
taiyangshenniao 2020-05-12
singer 2020-05-10
wangqing 2020-05-10
DCXabc 2020-05-01
austindev 2020-04-25
sunnyJam 2020-04-15
Harris 2020-04-15
VanTYS 2020-04-09
GoatSucker 2020-04-07