第7天主要是面向对象的内容。
学到现在越来越吃力了,从上节课开始博客就没时间写了,看看别人写的博客都那么棒。又想起了那句话比你牛逼的人都在努力,你却在放羊。。。唉,我写作业的效率有点低,以后得抓紧时间了。
摘要:
1. 面向对象的编程方法
2. 面向对象的三大特性
3. Python类相关知识
4. 反射(经常用到)
1. 面向对象的编程方法
1.1 什么是面向对象的编程方法?
面向对象编程(Object-Oriented Programming ):将所需要设计的对象抽象成各类具体的属性,它的集合就是类。开发正规的程序跟写一个运行一次就扔了的小脚本一个很大不同就是,你的代码总是需要不断的更改,不是修改bug就是添加新功能等,所以为了日后方便程序的修改及扩展,你写的代码一定要遵循易读、易改的原则(专业数据叫可读性好、易扩展)。其实OOP编程的主要作用也是使你的代码修改和扩展变的更容易。
1.2 为什么要面向对象编程?
是为了更好地处理:1)避免写重复代码,2)灵活的修改代码
1.3 面向对象编程与函数式编程的区别?
面向对象编程=>创建对象,通过对象执行方法
函数式编程=>执行函数
2. 面向对象的三大特性
面向对象的三大特性:封装、继承、多态
2.1 封装
封装可以分为两部分来理解:1)封装,2)调用封装
# 创建一个Person类 class Person(object): # 类的构造方法,实例化时自动执行 def __init__(self, name, age): self.name = name self.age = age # 实例化一个Person类的对象,自动执行类中的__init__方法 # 将alex和18封装到person1的name和age属性中 person1 = Person("alex", 18) # 实例化一个Person类的对象,自动执行类中的__init__方法 # 将qimi和20封装到person2的name和age属性中 person2 = Person("qimi", 20) # 调用alex的封装 print(person1.name) # 输出=> alex # 调用20的封装 print(person2.age) # 输出=> 20
2.2 继承
对于面向对象来说,继承其实就是将多个类共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法。
# 创建一个Person类 class Person(object): # 父类的构造方法,实例化时自动执行 def __init__(self, name, age): self.name = name self.age = age # 父类的方法 def print(self): print("{} is {} years old.".format(self.name, self.age)) # 定义一个Teacher的类,继承Person类 class Teacher(Person): # 不推荐这么写 # def __init__(self, habit): # super(Person, self).__init__() # self.habit = habit # 旧的写法,也不推荐这么写 # def __init__(self, name, age, habit): # Person.__init__(self, name, age) # self.habit = habit # 推荐写法,在子类的构造方法里只出现子类的类名 def __init__(self, name, age, habit): super(Teacher, self).__init__(name, age) self.habit = habit # 或者可以写得更简单一些,跟上面的写法是等价的。 def __init__(self, name, age, habit):
super().__init__(name, age) self.habit = habit
# 定义一个方法 def get_habit(self): print("The habit of {} is {}.".format(self.name, self.habit)) # 实例化一个老师 alex = Teacher("alex", 18, "Girl") # 调用父类的方法 alex.print() # 调用本类的方法 alex.get_habit() ============================================== output==> alex is 18 years old. The habit of alex is Girl.
多继承:
class D(object): def bar(self): print 'D.bar' class C(D): def bar(self): print 'C.bar' class B(D): def bar(self): print 'B.bar' class A(B, C): def bar(self): print 'A.bar' a = A() # 执行bar方法时 # 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错 # 所以,查找顺序:A --> B --> C --> D # 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了 a.bar()
当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类。
经典类(深度优先):首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中没有,则继续去C类中找,如果还是未找到,则报错。
新式类(广度优先):首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中没有,则继续去D类中找,如果还是未找到,则报错。
注意:①在上述查找过程中,一旦找到,则寻找过程立即中断,便不会再继续找了。
②Python3里为广度优先。
2.3 多态
# 定义父类Animal class Animal: def __init__(self, name): # Constructor of the class self.name = name # 定义一个供子类复写的方法 def talk(self): # Abstract method, defined by convention only raise NotImplementedError("Subclass must implement abstract method") # 定义子类Cat class Cat(Animal): def talk(self): return 'Meow!' # 定义子类Dog class Dog(Animal): def talk(self): return 'Woof! Woof!' # 实例化一个Cat、一个Dog animals = [Cat('Missy'), Dog('Lassie')] for animal in animals: print("{} : {}".format(animal.name, animal.talk()))
用Python实现多态
3. Python类的相关知识
3.1 字段(变量)
① 普通字段属于对象、静态字段属于类。
② 普通字段需要通过对象来访问、静态字段通过类访问。
③ 静态字段在内存中只保存一份、普通字段在每个对象中都要保存一份。
因此:通过类创建对象时,如果每个对象都具有相同的字段,那么就应该使用静态字段。
3.2 方法
方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
① 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self。
② 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls。
③ 静态方法:由类调用;无默认参数。
class Animal(object): def __init__(self, name): self.name = name self.num = None def say_hi(self): # 普通方法,由实例调用访问实例变量 print("Hi~ {}".format(self.name)) @classmethod # 类方法,不能访问实例变量 def talk(cls): print(" is talking.") @staticmethod # 静态方法,不能访问类变量及实例变量 def walk(): print("{} is walking.")
相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。
不同点:方法调用者不同、调用方法时自动传入的参数不同。
3.3 属性
class Animal(object): def __init__(self, name): self.name = name self.num = None @property # 把方法变成属性,可以调用实例变量 def habit(self): return "{}'s habit is eating.".format(self.name) @property # 定义类属性 def total_animals(self): return self.num @total_animals.setter # 给类属性赋值 def total_animals(self, num): self.num = num print("total animals: {}".format(self.num)) @total_animals.deleter # 删除类属性 def total_animals(self): del self.num print("total animals is deleted.") # 访问habit属性 a = Animal("tommie") # 访问类属性,调用时不需要加括号 print(a.habit) a.say_hi() # 打印类属性 print(a.total_animals) # 给类属性赋值 a.total_animals = 10 print(a.total_animals) # 删除类属性 del a.total_animals try: print(a.total_animals) except AttributeError as e: print(e) ============================================== output: tommie's habit is eating. Hi~ tommie None total animals: 10 10 total animals is deleted. 'Animal' object has no attribute 'num'
实例:对于主机列表页面,每次请求不可能把数据库中的所有内容都显示到页面上,而是通过分页的功能局部显示,所以在向数据库中请求数据时就要显示的指定获取从第m条到第n条的所有数据(即:limit m,n),这个分页的功能包括:
① 根据用户请求的当前页和总数据条数计算出 m 和 n。
② 根据 m 和 n 去数据库中请求数据。
# ############### 定义 ############### class Pager: def __init__(self, current_page): # 用户当前请求的页码(第3页、第7页...) self.current_page = current_page # 每页默认显示10条数据 self.per_items = 10 @property def start(self): val = (self.current_page - 1) * self.per_items return val @property def end(self): val = self.current_page * self.per_items return val # ############### 调用 ############### p = Pager(1) p.start 就是起始值,即:m p.end 就是结束值,即:n
example code
""" 静态字段方式设置类属性 """ class Foo(object): def bar(self): return "john" # *必须两个参数 def set_bar(self, value): print("set value:{}".format(value)) def del_bar(self): print("delete value") BAR = property(bar, set_bar, del_bar, "description") obj = Foo() print(obj.BAR) # 自动调用第一个参数中定义的方法:get_bar obj.BAR = "alex" # 自动调用第二个参数中定义的方法:set_bar方法,并将“alex”当作参数传入 del Foo.BAR # 自动调用第三个参数中定义的方法:del_bar方法 print(obj.BAR.__doc__) # 自动获取第四个参数中设置的值:description...
静态字段方式设置类属性
4. 反射
""" 贯穿开发周期内都会用到的:hasattr,setattr,getattr,delattr的介绍 传说中的反射,通过名字的字符串获取相应对象的内存地址 """ import sys class WebServer(object): def __init__(self, host, post): self.host = host self.post = post def start(self): print("Server is starting...") def stop(self): print("Server is stopping...") def restart(self): self.stop() self.start() # 定义一个类外面的方法 def test_run(res): print("{} {} is running...".format(res.host, res.post)) if __name__ == "__main__": server = WebServer("localhost", 33) # 判断实例中是否有命令行参数同名的方法名 if hasattr(server, sys.argv[1]): func = getattr(server, sys.argv[1]) # 获取对应类方法的内存地址 # 执行上面获取的方法,可带参数。 func() # setattr # 把一个类外面的方法绑定到一个类对象中 setattr(server, "run", test_run) server.run(server) # delattr # 删除类方法 delattr(WebServer, "stop") print("=======" * 10) server.restart()
5.类的特殊成员
""" 类的特殊成员 """ class A(object): """ 这是一个测试的类。。。 """ pass # __doc__ 表示类的描述信息 print(A.__doc__) # __module__ 表示当前操作的对象在那个模块 a = A() print(A.__module__) # __main__ print(a.__module__) # __main__ # __class__ 表示当前操作的对象的类是什么 print(a.__class__) # <class '__main__.A'> # __init__ 构造方法,通过类创建对象时,自动触发执行 # __del__ 析构方法,当对象在内存中被释放时,自动触发执行。?用于关闭构造方法中打开的文件或数据库? # __call__ 实例对象后面加括号直接执行 # 构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()