一、什么是面向对象
- 面向过程:根据业务逻辑从上到下写垒代码
- 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
- 面向对象:对函数进行分类和封装,让开发“更快更好更强...”
优点:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。
缺点:
1. 编程的复杂度远高于面向过程,不了解面向对象而立即上手基于它设计程序,极容易出现过度设计的问题。一些扩展性要求低的场景使用面向对象会徒增编程难度,比如管理linux系统的shell脚本就不适合用面向对象去设计,面向过程反而更加适合。
2. 无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法准确地预测最终结果。于是我们经常看到对战类游戏,新增一个游戏人物,在对战的过程中极容易出现阴霸的技能,一刀砍死3个人,这种情况是无法准确预知的,只有对象之间交互才能准确地知道最终的结果。
应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方
面向对象的程序设计并不是全部。对于一个软件质量来说,面向对象的程序设计只是用来解决扩展性。
# 不是只有class才叫面向对象 def dog(name, gender): def jiao(): print('一只狗[%s]正在叫' % name) def chi_shi(): print('一只[%s]正在吃屎' % name) def init(): dog1 = { 'name': name, 'gender': gender, 'type': type, 'jiao': jiao, 'chi_shi': chi_shi } return dog1 return init() res = dog('alex', 'male') res['jiao']()
二、类与对象
类: 把一类事物相似的特征和动作整合到一起就是类,类是一个抽象概念
对象:基于类创建的具体事物(具体存在的)
在现实世界中:先有对象,再有类
世界上肯定是先出现各种各样的实际存在的物体,然后随着人类文明的发展,人类站在不同的角度总结出了不同的种类,如人类、动物类、植物类等概念
也就说,对象是具体的存在,而类仅仅只是一个概念,并不真实存在
在程序中:务必保证先定义类,后产生对象
这与函数的使用是类似的,先定义函数,后调用函数,类也是一样的,在程序中需要先定义类,后调用类
不一样的是,调用函数会执行函数体代码返回的是函数体执行的结果,而调用类会产生对象,返回的是对象
# 经典类 class Stu: pass # 新式类 class Stu(object): # (object),表示该类是从哪个类继承下来的,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。 pass
class Student(object): school = '123' # __init__方法是在对象产生之后才会执行,只用来为对象进行初始化操作,可以有任意python代码,但一定不能有返回值 def __init__(self, name, age): self.name = name self.age = age def learn(self): print('is learning') def eat(self): print('is eating') def sleep(self): print('is sleeping') # #注意: # 1.类中可以有任意python代码,这些代码在类定义阶段便会执行 # 2.因而会产生新的名称空间,用来存放类的变量名与函数名,可以通过Student.__dict__查看 # 3.对于经典类来说我们可以通过该字典操作类名称空间的名字(新式类有限制),但python为我们提供专门的.语法 # 4.点是访问属性的语法,类中定义的名字,都是类的属性 # 程序中类的用法 print(Student.__dict__) # {'__module__': '__main__', 'school': '123', '__init__': <function Student.__init__ at 0x000001C3519D4488>, # 'learn': <function Student.learn at 0x000001C3519D4510>, 'eat': <function Student.eat at 0x000001C3519D4598>, # 'sleep': <function Student.sleep at 0x000001C3519D4620>, # '__dict__': <attribute '__dict__' of 'Student' objects>, # '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None} # .专门用来访问属性,本质操作的就是__dict__ # Student.school # 等于经典类的操作Student.__dict__['school'] # Student.school = 'Oldboy' # 等于经典类的操作Student.__dict__['school']='Oldboy' # Student.x = 1 # 等于经典类的操作Student.__dict__['x']=1 # del Student.x # 等于经典类的操作Student.__dict__.pop('x') stu1 = Student('alex', 18) # 调用类,实例化得到对象 print(stu1.__dict__) # {'name': 'alex', 'age': 18} print(stu1.name) # 相当于stu1.__dict__['name'] stu1.sleep()
使用isinstance检查obj是否是类 cls 的对象
class A(object): pass class B(object): pass a = A() print(isinstance(a, A)) print(isinstance(a, (A, B))) # 可以传入一个元组,只要元组内有一个符合就返回True
三、静态属性、类方法、静态方法
1. 静态属性
把一个函数属性变成数据属性,只需要使用@property就可以了。
class School(object): def __init__(self, width, length): self.width = width self.length = length def cal_area(self): return self.width * self.length s = School(100, 100) print(s.cal_area()) class School(object): def __init__(self, width, length): self.width = width self.length = length @property def cal_area(self): return self.width * self.length s = School(100, 100) print(s.cal_area)
2. 类方法
类方法通过@classmethod装饰器实现,类方法和普通方法的区别是, 类方法只能访问类变量,不能访问实例变量。
# 访问实例变量 class Person(object): def __init__(self, name, country): self.name = name self.country = country @classmethod def nationality(self): print('Bigberg is %s.' % self.country) p = Person('Bigberg', 'CN') p.nationality() # 输出 Traceback (most recent call last): File "G:/python/untitled/study6/静态方法.py", line 31, in <module> p.nationality() File "G:/python/untitled/study6/静态方法.py", line 24, in nationality print('Bigberg is %s.' % self.country) AttributeError: type object 'Person' has no attribute 'country' # 访问类变量 class Person(object): country = 'Chinese' # 增加一个 全局属性/静态字段 def __init__(self, name, country): self.name = name self.country = country @classmethod def nationality(cls): # 这里将sefl 改为 cls print('Bigberg is %s.' % cls.country) p = Person('Bigberg', 'CN') p.nationality() # 输出 Bigberg is Chinese.
class Person(object): def __init__(self, name, country): self.name = name self.country = country def nationality(self): print('Bigberg is %s.' % self.country) Person.nationality(Person) # 需要一个参数 class Person(object): country = 'Chinese' # 增加一个 全局属性/静态字段 def __init__(self, name, country): self.name = name self.country = country @classmethod def nationality(cls): # 这里将sefl 改为 cls print('Bigberg is %s.' % cls.country) Person.nationality() # 不需要参数
3. 静态方法
在类中的方法前面通过@staticmethod装饰器即可把其装饰的方法变为一个静态方法
普通的方法,可以在实例化后直接调用,并且在方法里可以通过self.调用实例变量或类变量,但静态方法是不可以访问实例变量或类变量的,一个不能访问实例变量和类变量的方法,其实相当于跟类本身已经没什么关系了,它与类唯一的关联就是需要通过类名来调用这个方法。
class Person(object): def __init__(self, name): self.name = name @staticmethod def speak(): print('someone is speaking chinese.') # 静态方法在类中也不需要传入 self参数
静态方法是不能访问实例变量和类变量的
class Person(object): def __init__(self, name): self.name = name @staticmethod def speak(self): print('%s is speaking chinese.' % self.name) p = Person('Bigberg') p.speak() # 我们在 speak(self) 函数中传入 self # 事实上以上代码运行会出错的,说speak 需要一个self参数,但调用时却没有传递,没错,当speak变成静态方法后,再通过实例调用时就不会自动把实例本身当作一个参数传给self了。 Traceback (most recent call last): File "G:/python/untitled/study6/静态方法.py", line 26, in <module> p.speak() TypeError: speak() missing 1 required positional argument: 'self'
想让以上代码可以正常执行,有两种方法:
- 在调用时将实例本身传给 speak()
class Person(object): def __init__(self, name): self.name = name @staticmethod def speak(self): print('%s is speaking chinese.' % self.name) p = Person('Bigberg') p.speak(p) # 输出 Bigberg is speaking chinese.
- 在方法speak中去掉self,但这也意味着,不能通过self.调用实例中的其它变量了
class Person(object): def __init__(self, name): self.name = name @staticmethod def speak(): # 方法中已经没有 self 参数了 print('%s is speaking chinese.' % 'anyone') p = Person('Bigberg') p.speak() #输出 anyone is speaking chinese.