面向对象一
1 # 封装角色的属性 2 def Dog(name,s_type): 3 def hitperson(name): 4 print('dag %s is hitting person'%dic['name']) 5 dic={'name':name, 6 's_type':s_type, 7 'hitperson':hitperson} 8 return dic 9 10 def Person(name,age): 11 def hitdog(p): 12 print('person %s is hitting dog'%dic['name']) 13 dic={'name':name, 14 'age':age, 15 'hitdog':hitdog} 16 return dic 17 18 # 塑造具体形象 19 d1 = Dog('dddd','金毛') 20 d2 = Dog('erer','哈士奇') 21 p1 = Person('uiui',56) 22 d1['hitperson'](d1) 23 p1['hitdog'](p1)
面向过程VS面向对象
面向过程的程序设计核心是过程(流水线式思维),过程即解决问题的步骤
优点:极大地降低了写程序的复杂度,只需顺着要执行步骤堆叠代码即可
缺点:一条流水线就是用来解决一个问题,代码牵一发而动全身
应用场景:一旦完成基本很少改变的场景,Linux内核,git,以及Apache HTTP Server等
面向对象的程序设计核心是对象,使用一个又一个对象来完成某件具体的事件,且不用关心完成的具体过程
优点:使程序的维护和扩展更加简单,可以大大提高程序的开发效率
应用场景:需求经常变化的软件,企业内部软件,游戏等
类
# 1、类:是抽象的模板,用来描述具有相同属性和方法的对象的集合
# 2、实例是根据类创建出来的一个个具体的‘对象’,每个对象都拥有相同的方法,但各自的数据可能不同
# 使用class关键字来定义类,结构如下:
class ClassName():
pass
# 定义一个学生类:
class Student():
address = 'BeiJing'
def __init__(self,name,age):
self.name=name
self.age=age
def read(self):
pass
def learn(self):
pass
创建对象
# 通过调用类的实例化方法来创建一个类的实例(对象)
# python提供了一个def __init__(self):的实例化机制。任何一个类中,名字为__init__的方法就是类的实例化方法,具有__init__方法的类在实例化的时候,会自动调用该方法,并传递对应的参数,当通过'类名()'的方式创建对象时,当对象创建好之后会自动调用init方法
作用:是用来在对象被创建好之后,对对象的实例变量进行初始化赋值操作
如果init方法中除了self还有其他的参数,ClassName(a,b,c),a,b,c就会传递给init方法中声明的其他参数!
# zhangsan=Student('zhangsan',20)
init实例方法(构造方法):
用来在对象被创建好之后,对对象的实例变量进行初始化操作
当对象创建好之后,init就会被自动调用
classname()当这个语句执行后,表示init方法被调用
实例变量与类变量
# 4、实例变量
# 实例变量指的是实例(对象)本身拥有的变量。Student类中__init__方法里的变量就是实例变量
def __init__(self,name,age):
self.name = name
self.age = age
# 通过实例名加圆点的方式调用实例变量
# s1 = Student('zhangsan',20)
# s2 = Student('lisu',50)
# s1.name
创建实例变量的:
1)定义在init内部
2)在类的外部通过对象的引用动态创建
class Test():
pass
test = Test()
#在动态的给test对象添加新的实例变量
test.name = 'zhangsan'
test.age = 20
在类外部创建实例变量
class Test():
# name = zhangsan age = 20 #2
def __init__(self,name,age):
self.name = name
self.age = age
test = Test('zhangsan',20) #1
#此处动态添加的实例变量只会作用在当前对象中,不会触发在类模板中
test.school = 'xxx'
a = Test('lisi',30)
5、类变量:定义在类的内部方法的外部
# 在定义类中,构造方法之外的变量,称为类变量。类变量是所有实例共有的变量,每一个实例都可以访问类变量
# 类变量本质是属于类所有的,可以通过类名或者实例名加圆点的方式访问类变量,比如:
# print(Student.address) # 通过类名访问类属性
# print(s1.address,s2.address) # 对象访问类属性
# 修改类变量中存储的数据: # (1)通过类名修改 # class Student(): # address = 'BeiJing' # def __init__(self,name,age): # self.name=name # self.age=age # # def read(self): # pass # def learn(self): # pass # s1 = Student('sss',48) # s2 = Student('ddd',56) # print('修改前的类属性:',Student.address) # 修改前的类属性: BeiJing # Student.address = 'ShangHai' # 通过类名修改类属性 # print('修改后的类属性:',Student.address) # 修改后的类属性: ShangHai # print(s1.address,s2.address) # ShangHai ShangHai # (2)通过实例名访问类属性对其进行修改 (X) # print('修改前的类属性:',Student.address) # 修改前的类属性:BeiJing # s1.address = 'shanghai' # 通过实例名访问类属性对其进行修改 # print('修改后的类属性:',s1.address,s2.address) # 修改后的类属性:shanghai BeiJing ***********注意: 通过对象名打点的方式(s1.address)只可以访问类属性,不可以修改 # 如果直接通过对象名打点的方式修改类属性是不可以的,该种方式并不是对象属性进行修改,而是给该对象新增一个和类属性同名的实例变量
类的方法
1、实例方法
1 python的类中包含实例方法、静态方法、类方法三种,区别在于传入的参数和调用方式不同 2 3 # 在类的内部,使用def关键字来定义一个方法 4 # 1、实例方法 5 # 类的实例方法由实例调用,至少包含一个self参数,且为第一参数。执行实例方法时,会自动将调用该方法的实例赋值给self 6 # self代表的是类的实例,而并非类本身,只可以出现在类的内部被使用,self不是关键字 7 8 实例方法定义的语法结构: 9 class Test(): 10 def __init__(self): # 实例方法 11 pass 12 def methodName(self,xx,yy): # 实例方法 13 pass 14 15 16 # class Student(): 17 # address = 'BeiJing' # 类属性 18 # def __init__(self,name,age): 19 # self.name = name # 实例属性 20 # self.age = age # 实例属性 21 # def read(self): 22 # print(self) # self就是read方法的调用者对应的对象 23 # print('student can read!') 24 # def learn(self): 25 # pass 26 # s1 = Student('zhangsan',20) 27 # s1.read() # 不需要给self主动传参 28 # print(s1) 29 30 # 对象之间的交互:可以将一个对象作为另一个对象方法的参数进行传递 31 # class Dog(): 32 # def __init__(self,name,blood,agg_value): # agg_value为攻击值 33 # self.name = name 34 # self.blood = blood 35 # self.agg_value = agg_value 36 # 37 # def hitperson(self,p): # p是被攻击的人对象 38 # p.blood -= self.agg_value 39 # def show_blood(self): 40 # return self.blood 41 # 42 # class person(): 43 # def __init__(self,name,blood,agg_value): 44 # self.name = name 45 # self.blood = blood 46 # self.agg_value = agg_value 47 # def hitdog(self,d): # d是被攻击狗对象 48 # d.blood -= self.agg_value 49 # def showBlood(self): 50 # return self.blood 51 # 52 # dd = Dog('doudo',100,20) 53 # pp = person('susu',100,30) 54 # 55 # # dd去攻击pp 56 # dd.hitperson(pp) 57 # print(dd.show_blood(),pp.showBlood())
2、静态方法
# 静态方法由类调用,无默认参数。将实例化方法参数中的self去掉,在方法定义上方加上@staticmethod就是静态方法
# 静态方法属于类和实例无关,建议只使用类名.静态方法的调用方式(虽然也可以使用实例名.静态方法的方式调用)
静态方法中可以传参数,相当于一个函数
# class obj():
# def instanceFunc(self):
# print('i am instanceFunc')
# @staticmethod # 静态方法
# def staticFunc(y):
# print('i am statiaFunc%s'%y)
# obj.staticFunc(5) # 尽量使用类名调用类方法
# 运行结果:i am statiaFunc5
在静态方法中可不可以访问类的相关成员?
- 可不可以访问实例方法和实例变量?
- 在类的一个成员方法中想要访问实例变量或者实例方法必须基于self实现,但是在静态方法中不存在self,使用对象名.实例变量/方法可以获取
- 可不可以访问类方法和类变量?
- 可以.因为可以直接通过类名访问类变量和类方法.
3、类方法
# 类方法由类调用,采用@classmethod装饰,至少传入一个cls(代指类本身,类似self)参数,执行类方法时,自动将调用该方法的类赋值给cls。建议只使用类名.类方法的调用方式(虽然也可以使用实例名.类方法的方式调用)
cls表示当前类方法存在的那个类名
# class obj():
# def instanceFunc(self): # 普通的实例方法
# print('i am instanceFunc')
#
# @staticmethod
# def staticFunc(): # 静态方法
# print('i am statiaFunc')
#
# @classmethod # 装饰器
# def classFunc(cls): # 类方法,cls就是当前类
# print('i am classFunc')
#
# obj.classFunc() # 尽量使用类名调用类方法
面向对象的组合用法
1 # 组合指的是,在一个类中以另一个类的对象作为数据属性,称为类的组合 2 # 例子:一个学生会有一个手机,学生使用手机看电影 3 # class CellPhone(): 4 # def __init__(self,c_type): 5 # self.c_type = c_type 6 # 7 # def watchMovie(self,movie_title): 8 # print('watching %s....'%movie_title) 9 # 10 # class Student(): 11 # def __init__(self,sid,name): 12 # self.sid = sid 13 # self.name = name 14 # self.CellPhone = CellPhone('iphone8') 15 # 16 # s1 = Student(1101,'susu') 17 # s1.CellPhone.watchMovie('sdsd') 18 19 20 class Cellphone(): 21 def __init__(self,type): 22 self.type = type 23 24 def watchMovie(self,title): 25 return '%s手机正在观看%s'%(self.type,title) 26 27 class Student(): 28 def __init__(self,name): 29 self.name = name 30 self.cellphone = Cellphone('iphone8') 31 32 def func(self): 33 res = s.cellphone.watchMovie('Droaemon') 34 print('学生%s使用%s'%(self.name,res)) 35 36 s = Student('lili') 37 s.func() 38 39 40 对象间方法的交互 41 class Person(): 42 def __init__(self,name,blood,agg_value): 43 self.name = name 44 self.blood = blood 45 self.agg_value = agg_value 46 47 def hitDog(self): 48 d.blood -= self.agg_value 49 50 def show_value(self): 51 return self.blood 52 53 class Dog(): 54 def __init__(self,name,blood,agg_value): 55 self.name = name 56 self.blood = blood 57 self.agg_value = agg_value 58 59 def hitPerson(self): 60 p.blood -= self.agg_value 61 62 def show_value(self): 63 return self.blood 64 65 p = Person('lolo',100,20) 66 d = Dog('dodo',100,5) 67 d.hitPerson() 68 res = p.show_value() 69 print(res)
总结
# 类:就是一个抽象模板
# 对象也可以叫做实例
# 创建一个对象也可以叫做实例化一个对象
# 属性
# 1、类变量也可以叫做类属性,属于类所有,但是可以被所有的对象共享
# 所有的对象可以访问类属性,但是不可以通过对象的方式修改类属性
# 2、实例变量也叫做对象属性,属于对象所有的,只能通过对象来调用
# 方法:
# 1、实例方法也可以叫做对象方法,属于对象所有的,有一个比较特殊的方法就是init,可以叫做'构造方法'
# 其作用就是用来对对象的属性进行初始化赋值
# 2、类方法属于类所有,但是可以被所有的对象共享,最好使用类名调用类方法
# 3、静态方法就是在类中定义的一个普通的函数
面向对象的三大特性
封装、继承和多态
封装
#1、 封装:将数据与具体操作的实现代码放在某个对象的内部,是这些代码的实现细节不被外界发现且不能通过任何形式修改对象内部实现,正是由于封装机制
# 作用:
# 1)程序在使用某一对象时不需要关心该对象的数据结构细节即实现操作的方法。
# 2)使用封装能隐藏对象实现细节,使代码更易维护,同时因为不能直接调用、修改对象内部的私有信息,在一定程度上保证了系统安全性
继承
1 # 新的类称为子类,而被继承的类称为基类、父类或超类 2 # 子类获得了父类的全部变量和方法的同时,又可以根据需要进行修改、扩展 3 # 基本语法结构: 4 # class father(): 5 # pass 6 # class son(father): 7 # pass 8 # # 例:查看学生(子类)是否可以继承到人(父类)的属性和方法 9 # 思路:看类属性、对象属性和类方法、实例方法、静态方法是否都可以被继承(都可以继承) 10 # class Person(): # 父类 11 # classVar = 'classVar' 12 # def __init__(self,name,age): 13 # self.name = name # 对象属性 14 # self.age = age # 对象属性 15 # def read(self): 16 # print('read') 17 # @staticmethod 18 # def staticFunc(): 19 # print('satsicFunc') 20 # @classmethod 21 # def classFunc(cls): 22 # print('classFunc') 23 # 24 # class Student(Person): # 子类 25 # pass 26 # 27 # s1 = Student('ddd',18) 28 # 29 # print(Student.classVar) # 子类的类变量 30 # print(s1.name,s1.age) # 子类的实例变量 31 # s1.read() # 子类的实例方法 32 # Student.staticFunc() # 子类的静态方法 33 # Student.classFunc() # 子类的类方法 34 35 结论:父类中的所有的类变量,实例变量,类方法,静态方法,实例方法都可以继承给子类. 36 继承的初步作用:可以实现程序的复用
派生
# 3、派生:子类添加自己独有的方法或属性 子类除了可以继承父类的属性和方法之外还可以生成自己独有的属性和方法. # class Person(): # 父类 # classVar = 'value' # def __init__(self,name,age,sex): # self.name = name # self.age = age # self.sex = sex # def speak(self): # print('person can speak') # @classmethod # def classFunc(cls): # print('i am Person classFunc') # @staticmethod # def staticFunc(): # print('i am Person staticFunc') # # class Student(Person): # address = 'BeiJing' # 子类派生出来的类变量 # def __init__(self,classNum,score,name,age,sex): # # 使用super方法调用父类的构造函数,从父类中继承过来实例变量 # super().__init__(name,age,sex) # # className和score是子类派生出来的实例变量 # self.classNum = classNum # self.score = score # def learn(self): # print('learn') # # s1 = Student('susu',18,'f',5,89) # print(s1.classNum,s1.name,s1.age,s1.sex,s1.score) # print(Student.address) # * super()函数 # 通过super()函数调用父类的实例化方法__init__, # 作用:在子类派生实例变量时,在子类的构造方法中通过super调用父类的构造方法,从而可以让子类继承到父类的实例变量 # 语法: # super(子类名,self).方法名(),需要传入的是子类名和self,调用的是父类里的方法,按父类的方法需要传入参数。
重写
1 # 子类从父类中继承到的某个方法,可能满足不了子类的需求,则子类需要在继承到父类方法后,对该方法进行拓展(在原有父类方法的功能基础上新增添一些功能) 2 class Father(): 3 def __init__(self,h): 4 self.h = h 5 6 def hobby(self): 7 return self.h 8 9 class Son(Father): 10 def __init__(self,h,a1,a2): 11 super().__init__(h) 12 self.a1 = a1 13 self.a2 = a2 14 15 def hobby(self): 16 li = [super().hobby()] 17 # 在继承父类原始方法操作的基础上新增添一些操作经即可 18 li.append(self.a1) 19 li.append(self.a2) 20 return li 21 22 s = Son('SONG','lolo','tyty') 23 res = s.hobby() 24 print(res)
继承中相关的self和cls的区别:
1 class SuperClass(): 2 def normalFunc(self): 3 print(self) 4 @classmethod 5 def classFunc(cls): 6 print(cls) 7 class SubClass(SuperClass): 8 pass 9 10 #实例化了一个子类对象 11 sub = SubClass() 12 13 sub.normalFunc() # self表示就是子类对象 14 SubClass.classFunc() # cls表示的一定是子类类名 15 运行结果: 16 <__main__.SubClass object at 0x000002B05E544AF0> 17 <class '__main__.SubClass'> 18 19 # 父类对象 20 father_obj = SuperClass() 21 22 father_obj.normalFunc() 23 SuperClass.classFunc() 24 运行结果: 25 <__main__.SuperClass object at 0x000002B05E54F0D0> 26 <class '__main__.SuperClass'>
单继承与多继承
# 单继承:
# 继承的作用:实现了程序的高复用,大大缩短程序的开发周期
# 多继承:
# 子类在调用某个方法或变量的时候,首先在自己内部查找,如果没有找到,则开始根据继承机制在父类里查找,根据类的父亲定义中的顺序,以深度优先的方式逐一查找父亲
1 # (1)B和E类中各自有一个相同的show方法,则A继承B和E后, 2 # 直接调用show,则最终执行的show到底是B的还是E的? 3 # class D(): 4 # pass 5 # class C(D): 6 # pass 7 # class B(C): 8 # def show(self): 9 # print('B.....show') 10 # class G(): 11 # pass 12 # class F(G): 13 # pass 14 # class E(): 15 # def show(self): 16 # print('E.....show') 17 # class A(B,E): 18 # pass 19 # 20 # A().show() # B.....show 21 22 # 结论:在A的定义中,继承参数的书写有先后顺序,写在前面的被优先继承 23 24 # (2)如果B没有show方法,而是D有呢? 25 # class D(): 26 # def show(self): 27 # print('D.....show') 28 # class C(D): 29 # pass 30 # class B(C): 31 # pass 32 # class G(): 33 # pass 34 # class F(G): 35 # pass 36 # class E(): 37 # def show(self): 38 # print('E.....show') 39 # class A(B,E): 40 # pass 41 # A().show() # D.....show B会继承D中的show方法 42 43 # (3) 44 # class H(): 45 # def show(self): 46 # print('H....show') 47 # class D(H): 48 # pass 49 # class C(D): 50 # pass 51 # class B(C): 52 # pass 53 # 54 # class G(H): 55 # def show(self): 56 # print('G.....show') 57 # class F(G): 58 # pass 59 # class E(F): 60 # pass 61 # class A(B,E): 62 # pass 63 # 64 # A().show() # G.....show
type与instance
1 # type和isinstance 2 #(1) isinstance()函数来判断一个对象是否是一个已知的类型,类似type() 3 # 语法: 4 # isinstance(object,classinfo) 5 # 参数: 6 # object----实例对象 7 # classinfo----可以是类名、基本类型或者由他们组成的元组 8 # 返回值:如果对象的类型与参数二的类型(classinfo)相同则返回True,否则返回False 9 10 #(2) type:返回对象的类型 11 # 两者区别: 12 # type()不会认为子类是一种父类类型,不考虑继承关系 13 # isinstance()会认为子类对象是一种父类类型,考虑继承关系 14 15 # class A(): 16 # pass 17 # class B(A): 18 # pass 19 # a=10 20 # b=B() 21 # print(b) #<__main__.B object at 0x000001B791085A30> 22 # print(A) # <class '__main__.A'> 23 24 # print(type(a),isinstance(a,int)) # <class 'int'> True 25 # # 在面向对象,一个子类对象可以被视为父类类型 26 # print(type(b),isinstance(b,A)) # <class '__main__.B'> True 27 print(type(b),isinstance(B,A)) # <class '__main__.B'> False
接口类
# 继承有两种用途:
# 1、继承基类的方法,并且做出自己的改变或者扩展(代码重写)
# 2、实现接口类
# 接口类:(方法名、参数个数固定,不可以实例化对象)
# 接口类实际上就是一个规范子类的类,只不过这个类与别的类不太一样,接口类内部的方法本身不实现,子类继承接口类
# 子类需要实现接口的功能,否则无法正常工作。
# 作用:规范好子类的接口
# 在python3中,如果定义一个类,该类没有明确写出父类是谁,那么默认情况下,这个类的父类为object类
# 因此我们认为object类就是所有自定义类的“元类”
接口类的实现:
1.导入模块,让接口类的元类为ABCMeta
2.在接口类中定义抽象方法
使用@abstractmethod装饰器装饰的方法,且该方法不可以有实现.
接口类对子类的规范就是由抽象方法来表示/实现
class AbsObj(ABCMeta):
@abstractmethod
def fun1(self):
pass
1 # from abc import ABCMeta,abstractmethod # 从abc模块导入ABCMeta这个元素 2 # class GraphicRule(metaclass=ABCMeta): #接口类 3 # @abstractmethod 4 # def area(self,length,width): 5 # pass 6 # @abstractmethod 7 # def perimeter(self,length,width): 8 # pass 9 # 10 # class Rectangle(GraphicRule): # 子类继承了接口类后,才可以实现接口类中指定好的规范 11 # def __init__(self,length,width): 12 # self.length = length 13 # self.width = width 14 # 15 # # 对父类这个接口类中指定好的规范进行是实现 16 # def area(self,length,width): 17 # return length*width 18 # def perimeter(self,length,width): 19 # return (length+width)*2 20 # def fun(self): 21 # print('ddfff') 22 # r = Rectangle(2,3) 23 print(r.area(2,3)) # 6 24 r.fun() # ddfff
抽象类
# 抽象类:只能被继承不能被实例化 # 如果说类是从一堆对象中抽取相同的内容而来的, # 那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性 # 抽象类与普通类的不同之处在于:抽象类中有抽象方法,该类不能被实例化,自能被继承,且子类必须实现抽象方法。 from abc import ABCMeta,abstractmethod class Graphic(metaclass = ABCMeta): @abstractmethod def area(self,width,length): pass @abstractmethod def func2(self,width,length): pass def normalFunc(self): print('i am normalFunc') class func(Graphic): def __init__(self,width,length): self.width = width self.length = length def area(self,width,length): return width * length def func2(self,width,length): return width + length def funca(self,i): print('i am funca中的%s'%i) f =func(2,3) print(f.area(2,3)) print(f.func2(2,3)) f.funca(5) 结果: 6 5 i am funca中的5 # 抽象类和接口类的同异: # 相同: # 都有抽象方法 # 都不可以实例化 # 不同: # 抽象类:约束子类中必须包含某些方法 弱约束 # 接口类:约束子类中必须包含父类规定的方法 强约束
多态
# 面向对象的三大特性--多态
一类事物有多种形态,比如动物有多种形态:猫、狗、猪等
多态:一个操作会返回多种状态,使猫、狗、猪等继承动物这个父类,可返回多个小动物的值。
# 多态性指的是可以在不用考虑对象具体类型的情况下而直接使用对象、这就需要在设计时,把对象的使用方法统一成一种:例如cat、dog、pig都是继承了动物类
# 并各自重写了talk方法,无论给Animal传递的是猫狗猪都能正确调用相应的方法
# 多态的好处在于增强了程序的灵活性和可扩展性
# 多态的本质在于不同的类中定义有相同的方法名,python是伪多态
之所以称为伪多态是因为在调用方法时,python并未设置类似只限制继承了Animal这个父类且含有这个方法的子类输出返回值,而是将代码中所有包含这个talk方法的返回值全部输出
1 # class Animal(): 2 # def talk(self): 3 # pass 4 # 5 # class Dog(Animal): 6 # def talk(self): 7 # print('汪汪汪') 8 # 9 # 10 # class Cat(Animal): 11 # def talk(self): 12 # print('喵喵喵') 13 # 14 # class Pig(Animal): 15 # def talk(self): 16 # print('哼哼哼') 17 18 # class Car(): 19 # def talk(self): 20 # print('嘀嘀嘀') 21 22 #具有多态性质的函数 23 def giveAnimal(a): 24 a.talk 25 26 # # 实例化三个对象 27 # dog = Dog() 28 # cat = Cat() 29 # pig = Pig() 30 giveAnimal(dog) 31 giveAnimal(cat) 32 33 # 由于python的动态语言特性,传递给函数talk()的参数可以是任意类型 34 # 只要它有一个talk()方法即可。动态语言调用实例方法是不检查类型,只要方法存在 35 # 参数正确,就可以调用,这就是动态语言的"鸭子类型"
类方法与静态方法
类方法和静态方法的作用
# python中的__new__(cls)和__init__(self)的区别:
# 同:二者均为python面向对象语言中的函数,__new__比较少用,__init__则用的比较多
# 异:__new__是一个类方法,是在实例创建之前被调用的,因为他的任务就是创建实例然后返回该实例对象
# __init__是当实例对象创建完成后被调用的,然后设置对象属性的一些初始值,
# 通常用在初始化一个类实例的时候。是一个实例方法 __new__先被调用,__init__后被调用,__new__的返回值(实例)将传递给__init__方法的第一个参数,然后__init__给这个实例设置一些参数
在实例方法的参数中,第一个参数必须是self,self表示的是对象,那么这个对象就是有new方法创建好且返回过来的
new方法如何创建一个实例对象?
只需要在new方法中返回:
return super().__new__(cls)
class Test():
def __init__(self):
print('i am init')
def __new__(cls, *args, **kwargs):
print('i am new')
test = Test()
#程序输出的结果只有:i am new
#分析:为什么init没有被调用?
因为我们知道,只有当对象创建好之后,init才会被调用.现在init没有被调用只能说明对象没有被创建好.
#思考:为什么没有创建好对象?
因为对象的创建是由new方法实现,new需要创建好对象,return super().__new__(cls),然后将其返回给init的self.
# class Test():
# # 如果self没有接收到new的返回值,则init是不会被调用的
# def __init__(self): # self就是用来接收new方法的返回值
# print('i an init')
# def __new__(cls, *args, **kwargs):
# print('i am new')
# # 返回一个创建好的对象,只不过该对象是由父类object方法的new创建好
# return super().__new__(cls)
# t = Test()
# 打印结果:
# i am new
# i an init
(1)类方法
1 # 例题:学生类继承班级类,每实例化一个学生,班级人数都能增加 2 # class StuClass(): 3 # num = 0 4 # @classmethod 5 # def add(cls): 6 # cls.num += 1 7 # 8 # @classmethod 9 # def show_num(cls): 10 # print('总人数:',cls.num) 11 # 12 # def __new__(cls, *args, **kwargs): 13 # cls.add() 14 # return super().__new__(cls) 15 # 16 # class student(StuClass): 17 # pass 18 # 19 # s1 = student() 20 # s2 = student() 21 # s3 = student() 22 # student.show_num()
静态方法
1 2、静态方法是类中的'函数',静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但是和类本身没有关系 2 静态方法相当于一个函数,可以传参数 3 # 也就是说在静态方法中,不会涉及到类中的属性和方法操作。可以理解为,静态方法是个独立的单纯的函数,仅仅托管于某个类的名称空间里,便于使用和维护 4 # 例题:定义一个关于时间操作的类,其中有一个获取当前时间的函数 5 # import time 6 # class TimeTest(object): 7 # def __init__(self, hour, minute, second): 8 # self.hour = hour 9 # self.minute = minute 10 # self.second = second 11 # 12 # @staticmethod 13 # def showTime(): 14 # return time.strftime("%H:%M:%S", time.localtime()) 15 # 16 # print(TimeTest.showTime()) # 17:37:13 17 # t = TimeTest(2, 10, 10) 18 # nowTime = t.showTime() 19 # print(nowTime) # 17:37:13 20 21 # 如上,使用了静态方法(函数),然而静态方法实现中并没有使用实例的属性和方法
成员保护和访问限制
1 # 在类的内部,有各种变量和方法。这些数据成员,可以在类的外部通过实例或者类名进行调用 2 # class Student(): 3 # def __init__(self,name,age): 4 # self.name = name 5 # self.age = age 6 # 7 # s1 = Student('koko',56) 8 # print(s1.name,s1.age) 9 10 # 但往往我们不希望所有的变量和方法被外部访问,需要针对性的保护某些成员(账户余额),限制对这些成员的访问这样的程序才是可靠的、且符合业务逻辑的 11 12 # 在python中,如果要让内部成员不被外部访问,可以在成员的名字前加上两个下划线__,这个成员就变成一个私有成员,私有成员只能在类的内部访问,外部无法访问,私有成员无法被继承 13 14 # 私有成员只能在类的内部访问,外部无法访问 15 # class Student(): 16 # def __init__(self,name,age): 17 # self.__name = name 18 # self.age = age 19 # s1 = Student('koko',56) 20 # print(s1.name,s1.age) # 'Student' object has no attribute 'name' 21 22 # 外部如果要对__name进行访问和修改,在类的内部创建外部可以访问的get和set方法 23 # class Student(): 24 # def __init__(self,name): 25 # self.__name = name 26 # def setName(self,name): 27 # self.__name = name 28 # def getName(self): 29 # return self.__name 30 # s1 = Student('koko') 31 # print(s1.getName()) # koko 32 # s1.setName('1234') 33 # print(s1.getName()) # 1234 34 # 这样做不但对数据进行了保护的同时也提供了外部访问的接口, 35 # 而且在getName,setName这些方法中,可以额外添加对数据进行检测、处理、加工等等各种操作 36 37 # 本质上,从内部机制原理讲,外部不能直接访问__name是因为python解释器对外把__name变量改成了_Student__name,也就是_类名__name,因此,可以通过_Student__name在类外部访问__name,所以python中的私有成员是一个伪机制。 38 # class Student(): 39 # def __init__(self,name): 40 # self.__name = name 41 # # 42 # s1 = Student('koko') 43 # print(s1._Student__name) # koko 44 # 也就是说:python的私有成员和访问机制是'假的',没有从语法层面彻底限制对私有成员的访问 45 46 私有成员继承的问题:私有成员无法被继承 47 class Father(): 48 def __init__(self,name): 49 self.__name = name 50 def __normalFunc(self): 51 print('i am normalFun') 52 class Son(Father): 53 pass 54 55 s = Son('bobo') 56 print(s._Son__name) 57 s._Son__normalFunc() 58 运行结果报错:AttributeError: 'Son' object has no attribute '_Son__name'
魔术方法和属性
1、__init__():实例化方法,通过类创建实例时,自动触发执行
2、__class__:表示当前操作的对象是与哪个类 # print(s1) # <__main__.Student object at 0x0000023F3D920610> print(s1.__class__) # <class '__main__.Student'> # print(s1.__class__.__name__) # Student 返回对象类名
3、__del__():析构方法,当对象在内存中被释放时,自动触发此方法 # 注:此方法一般无需定义,因为python自带内存分配和释放机制,除非你需要释放的时候指定做一些动作。 # 析构函数的调用是由解释器在进行垃圾回收时自动触发执行的 # class Student(): # def __del__(self): # print('i am del') # s1 = Student() # print('i am print') # 运行结果 # i am print # i am del
4、__dict__:列出类或者对象中的所有成员,python自建,无需用户自己定义 class Student(): def __init__(self, name): self.__name = name def setName(self, name): self.__name = name def getName(self): return self.__name s1 = Student('koko') print(s1.__dict__) # {'_Student__name': 'koko'} print(Student.__dict__) # {'__module__': '__main__', '__init__': # <function Student.__init__ at 0x0000021817238700>, 'setName': <function Student.setName at 0x0000021817238790>, # 'getName': <function Student.getName at 0x0000021817238820>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}
5、__str__:如果一个类中定义了__str__()方法,那么在打印对象时,默认输出该方法的返回值 # 如果没有定义__str__()方法,打印结果是:<__main__.Foo object at 0x000000000210A358> # 打印一个对象,要求输出结果为"i love python" class obj(): def __str__(self): return 'i love python!' c = obj() print(c)
# 6、__getitem__()、__setitem__()、__delitem__() # python中标识符后面加圆括号,通常代表执行或调用的意思,而在标识符后面加中括号[],通常代表取值的意思 # python设计了__getitem__()、__setitem__()、__delitem__这两个特殊成员,用于执行与中括号有关的动作,分别表示:取值、赋值、删除数据 # class MyDic(): # def __init__(self): # self.d = {} # def __setitem__(self, key, value): # self.d[key] = value # print('i am setItem') # def __getitem__(self, item): # print('i am getItem') # return self.d[item] # d = MyDic() # d['name'] = 'koko' # print(d['name']) # 运行结果: # i am getItem # i am setItem # koko
7、__slots__ # python作为一种动态语言,可以在类定义完成和实例化后, # 给类或者对象继续添加随意个数或者任意类型的变量或方法,这就是动态语言的特性 # 但当我们像限制实例可以添加的变量,可以使用__slots__限制实例的变量 # 比如,只允许foo的实例添加name和age属性 # class Test(): # 规定了该类实例化的对象只可以添加slots规范好的属性 # __slots__ = ('name','age') # t = Test() # t.name = 'koko' # t.age = 58 # t.sex = 'f' # Test' object has no attribute 'sex' # 需要提醒的是,__slots__定义的属性仅对当前类的实例起作用,对继承了它的子类是不起作用的 # 当子类中也定义了__slots__,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__
# 8、__call__ # 如果为一个类编写了该方法,那么在该类的实例后面加括号,会调用这个方法 # 注:构造方法的执行是由类加括号执行的,即:对象=类名(), # 而对于__call__()方法,是由对象后加括号触发的,即:对象()或者类() class Test(): def __call__(self, *args, **kwargs): print('i am call') t = Test() t() # i am call
reflect反射
# reflect反射 # 首先区分两个概念:'标识名'和'字符串',前者是函数func的函数名,后者是“func”字符串,两者是不同事物, # 我们可以用func()的方式调用函数func,但我们不能用"func"()的方式调用函数。 # 例题:需要根据用户输入的url不同,调用不同的函数,实现不同的操作,也就是一个WEB框架的url路由功能 # 首先有一个commons.py文件,里面的函数分别用于展示不同的页面 # def index(): # return '首页页面' # def login(): # return '登录页面' # def main(): # return '主页面' # def regist(): # return '注册页面' # 其次,有一个visit.py文件,作为程序入口,接受用户输入,并根据输入展示相应的页面 # import commons # def main(): # page_text = input('enter a url:') # if page_text == 'index': # print(commons.index()) # elif page_text == 'login': # print(commons.login()) # elif page_text == 'main': # print(commons.main()) # elif page_text == 'regist': # print(commons.regist()) # else: # print('404') # 问题1:当url成百上千时用此方法便不合理, # 观察发现用户输入的url字符串和相应调用的函数名一致, # 如果可以用字符串直接调用函数就好了,但字符串不能直接调用函数,为了解决这个问题 # python提供了反射机制 # 解决方法: # getattr()函数的使用方法:接收2个参数,前面的是一个类或者模块,后面是一个字符串 # func = getattr(commons,inp)语句是关键,通过getattr()函数,从commons模块里,查找到和inp字符串"外形"相同的函数名,并将其返回,然后赋值给func变量。变量func此时就指向那个函数,func()就可以调用该函数 # 问题2:如果输入一个非法url,比如jpg,由于在commons里没有同名的函数,会报错 # 解决方法: # python提供了一个hasattr()的内置函数,用法和getattr()基本类似, # 它可以判断commons中是否具有某个成员,返回True或false # visit.Py # def main(): # page_text = input('enter a url:') # if hasattr(commons,page_text): # result = getattr(commons,page_text) # print(result()) # else: # print('404') # main()
setattr()、getattr()、delattr()函数
getattr函数的使用方法:接收2个参数,前面的是一个类或者模块,后面的是一个字符串,是字符串!!! func = getattr(commons,inp)语句是关键,通过getattr()函数,从commons模块里,查找到和inp字符串“外形”相同的函数名,并将其返回,然后赋值给func变量。变量func此时就指向那个函数,func()就可以调用该函数 这个过程就相当于把一个字符串变成一个函数名的过程 # 语法: # getattr(object,name) # 参数: # object----对象 # name -----字符串,对象属性 # return:value ----属性值 # visit.py def main(): page_text = input('enter a url:') if hasattr(commons,page_text): result = getattr(commons, page_text) print(result()) else: print('404') main() # setattr()函数对应函数getattr(),用于设置对象的属性值,该属性不一定存在 # setattr(object,name,value) # 参数: # object-------对象 # name --------字符串,对象属性 # value--------属性值 # 使用:设置真实存在的属性值 # class Stu(): # def __init__(self,name): # self.name = name # s = Stu('koko') # print(s.name) # koko # setattr(s,'name','koko') # print(s.name) # koko # 如果属性不存在会创建一个新的对象属性,并对属性赋值 # class Stu(): # def __init__(self,name): # self.name = name # s1 = Stu('koko') # setattr(s1,'age',20) # print(s1.name,s1.age) # koko 20 # delattr()函数用于删除属性 # 语法: # delattr(object,name) # 参数: # object----对象 # name------必须是对象属性 # 使用: # class Stu(): # def __init__(self,name,age): # self.name = name # self.age = age # s = Stu('nono',20) # print(s.name,s.age) # nono 20 # # delattr(s,'age') # print(s.name,s.age) # 'Stu' object has no attribute 'age'
单例模式
# 单例模式是一种常用的软件设计模式,通过单例模式可以保证系统中一个类只有一个实例而且该实例易被外界访问, # 从而方便对实例个数的控制并节约系统资源 # 如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案 # 要点: # 1、某个类只能有一个实例 # 2、必须自行创建这个实例 # 3、必须自行向整个系统提供这个实例 # 分析: # 一个对象实例化过程先执行类的__new__方法,默认调用object的__new__方法, # 返回一个实例化对象,然后再调用__init__方法,对这个对象进行初始化 # 在一个类的__new__方法中,先判断是不是存在实例,如果存在实例,就直接返回, # 如果不存在实例就创建 # hasattr()的内置函数,它可以判断commons中是否具有某个成员,返 # 回True或False # class Test(): # def __init__(self): # pass # def __new__(cls, *args, **kwargs): # if not hasattr(cls,'instance'): # 判断cls中是否存在instance # cls.instance = super().__new__(cls) # 创建类变量cls.instance # return cls.instance # # t1 = Test() # t2 = Test() # t3 = Test() # print(id(t1),id(t2),id(t3)) # 2883220144384 2883220144384 2883220144384