python3.x Day5 面向对象
类:
类是指:对具有相同属性的事物的抽象、蓝图、原型。在类中定义了这些事物都具备的属性和共同的方法。
对象:
一个对象就是一个类实例化以后的实例,一个类必须经过实例化后才能在程序中被使用,一个类可以实例化多个对象,每个对象亦有不同的属性,
比如人类指所有人,每个人是指具体的对象,人与人之间有共性,亦有不同
特性(好处):
封装:
在类中对数据的赋值、内部调用等 对外部用户是透明的,这也就使类变成了一个胶囊或容器,里边包含着类的数据和方法。
防止数据被随意修改
使外部程序不再需要关注对象内部的构造,仅需要此对象对外提供的接口进行直接访问即可,调用变得简单。
继承:
一个类可以派生出子类,父类中定义的属性和方法,自动在自类中可以使用。
通过父类-->子类的方式以最小代码量的方式实现不同角色的共同点和不同点的同时存在。
多态:
一个接口多种实现,在一个基类派生不同的子类,一个子类在继承了父类的方法名同时,又对父类的方法进行了不同的实现。
#语法及使用: class dog(object): fingers=5 def __init__(self,name): #构造函数,构造方法==初始化方法,实例化过程就调用这个。self是必须传的 公有属性中有描述 self.NAME=name #定义成员属性 self关键字 def sayhi(self): #类的方法 print("my name is %s"%self.NAME) d1=dog("yuanwei") #相当于 dog(d1,"yuanwei") self 就是本实例。 d2=dog("laopeng") #实例化后的对象叫实例,就是那个类的实例,self就是实例本身, d1.sayhi() d2.sayhi() print(d1.fingers) #实例没有fingers这个成员变量(实例属性),所以引用类的公共属性fingers print(d2.fingers) dog.fingers=4 #类的公共属性fngers变更值, print(d1.fingers) #实例中仍然没有fingers这个成员属性,所以,还是引用了公共属性fingers print(d2.fingers) d1.fingers=3 #这里是在外部实例自己定义了自己的成员变量fingers属性,这就和公共属性fingers无关了 print(d1.fingers) print(d2.fingers) dog.fingers=1 print(d1.fingers) #这里类的公共属性值变更了,但是再也和d1的成员属性fingers无关了 print(d2.fingers) d1.ending="yao yao yao" #这里证实实例的成员属性可以在外部重新定义。 print(d1.ending) def sayhi2(self): #外部定义一个额外的方法,目的是想用来作为d2的成员方法,顺便,替代d2的引用类公共方法。 print("%s eat meat"%self.NAME) d2.sayhi=sayhi2 d2.sayhi(d2) #这里可以看到他的方法实现,已经不再是类的公共方法了,而是外部定义的那个 d1.sayhi()
# 类 --> 实例化 --> 实例对象
# __init__() #构造函数
# self.name=name #属性,成员变量,字段
# def sayhi() #方法,动态属性
# 私有属性
# __private_attr_name = value #这就是定义了私有属性。
# def get_heart(self):#对外提供只读接口
# return self.__private_attr_name
# r1._dog__heart #强制访问实例的私有属性
# 公有属性
# 当所有属于本类的实例对象,都可以访问的属性
# 类中直接定义的,就是共有属性,不是在__init__()方法中定义
# 修改类的公有属性 只能使用类名.属性=值的方式,如果用实例去改,就会在实例的成员属性中,多出一个与类属性(公有属性)同名的成员属性
#类里定义的方法 也都是公共属性。。。。。传进去的self蔡代表自己的
# 想定义实例自己的方法,只能在外部定义一个方法,然后让实例方法引用,改成新的外部方法,就行了,就变成了这个实例独有的方法。
#析构方法 class peple(object): nationality="CN" def __init__(self,name): #构造方法,实例诞生时使用 self.name=name def __del__(self): print(self.name,"said : i'm died!!!") #析构方法,实例销毁时使用(实例名到内存存储的指针释放,就执行,后续垃圾清空和这个无关) #析构函数,一般可以用来做程序的收尾工作,关闭各种,一般是用于意外发生时 wangyue=peple("wangyue") del wangyue import time time.sleep(5)
类的继承:
class person(object): #父类 def __init__(self,name,sex): self.name=name self.sex=sex self.drink="drinking water" def talk(self): print("persion is talking") class black_persion(person): #继承父类的子类,子类继承父类的所有属性,和方法,包括构造方法等等等等, def __init__(self,name,sex,legs): #想要给子类新增其他实例属性,就需要重写父类构造方法,构造时, # 引用父类的构造方法,保持原父类属性,之后新增新的实例属性,重构不是覆盖,重构描述的是子类行为,父类又没改, # 子类愿意自己定义啥,父类也无所谓,儿子不愿意随父亲的姓也行,还是父亲的儿子,父亲的姓也不会因为儿子不跟就改变 person.__init__(self,name,sex) self.legs=legs def talk(self): #重写了父类的talk方法,如果不重写,就用父类的。 print("i'm talking") def walk(self): #也可以自己再定义其他方法。 print("i'm walking",self.name,self.sex,self.drink," walk use",self.legs) b=black_persion("wangyue","1","lllllllll") b.talk() b.walk()
class SchoolMember(object): count=0 def __init__(self,name,sex,age): self.name=name self.sex=sex self.age=age self.enroll() def enroll(self): SchoolMember.count +=1 def del_menber(self,member): print("[%s] has deleted !!!"%member.name) def tell(self): print("---------[%s] info---------"%self.name) for k,v in self.__dict__.items(): print("\t %s : %s"%(k,v)) print("-----------end--------------") def __del__(self): print("%s killed"%self.name) SchoolMember.count -=1 class Teacher(SchoolMember): def __init__(self,name,sex,age,shouru): # SchoolMember.__init__(self,name,sex,age) #经典类写法 super(Teacher,self).__init__(name,sex,age) #和写父类构建函数一样的,这叫新式类写法 self.shouru=shouru class Student(SchoolMember): def __init__(self,name,sex,age,fee): SchoolMember.__init__(self,name,sex,age) self.fee=fee t1=Teacher("wangyue",1,20,50000) s1=Student("laopeng",1,24,100) s2=Student("aliang",1,3,20) t1.tell() s1.tell() print("members : %s"%t1.count) del s2 print("members : %s"%t1.count) import time time.sleep(3)
类的多肽(好像用的不是特别多)
class Animal(object): def __init__(self,name): self.name=name def talk(self): raise EnvironmentError("must be shixian") class Dog(Animal): def talk(self): return "wang wang wang" class Cat(Animal): def talk(self): return "miao miao miao" d1=Dog("laopeng") c1=Cat("aliang") def animal_talk(obj): print(obj.talk()) animal_talk(d1) animal_talk(c1)
新式类 VS 经典类:
尽量使用新式类写法,经典类写法可以认为是Python支持,但不建议使用了,新式类写法中,实际上python封装了很多,之后记录的内容可以看出,比如构造过程,直到反射什么的都有影响呢。
class Person(object): #新式类写法 super pass class Person: #经典类写法 ParentClass.__init__() pass # 新式类是从python最基类继承而来,未来python提供对类更多的功能扩展,方法都会放在object类里 # # 多继承时顺序的区别,python2中 广度查询,多继承时,依照左先顺序继承,没有明确,就继续深度上找父类,找不到父类,再找右侧的第一级 # # python3 继承顺序没区别,是广度优先查,按照左先,找不到第一个就找第二个继承,都没有,再深度,找父类
特殊的成员方法:
class Dog(object): '''这是一个用于描述狗的类''' def __new__(cls, *args, **kwargs): #__new__()方法是用来实例化的方法, # 运行后会调用各种东西,包含__init__()方法,所以一般返回类的__new__()方法, # 用来实例化之前做一些事情。 print("这里是%s的__new__()方法"%cls.__module__) return object.__new__(cls) def __init__(self,name): #构建函数 self.name=name self.test_data={} def __del__(self): #析构函数 print("i'm killed:%s"%self.name) def my_name(self): print("my name is %s"%self.name) def __call__(self, *args, **kwargs): #这个__call__()方法是实例可以直接被调用 实例() 时候会用到,应该还有更牛的地方 print("hello dog ",args,kwargs) def __str__(self): #直接打印对象时候,会用这个返回信息,默认是内存地址。 return "name:%s"%self.name def __setitem__(self, key, value):#让类的实例像字典一样来处理一些数据,本例使用一个实例属性test_data来存的, # 这个__setitem__()方法是来接收kv设置字典 print("__setitem__()方法") self.test_data[key]=value def __delitem__(self, key):#让类的实例像字典一样来删除一些数据 print("__delitem__()方法") del self.test_data[key] def __getitem__(self, key):#让类的实例像字典一样访问数据 return self.test_data[key] print(Dog.__doc__) #__doc__属性 是用来描述这个类的描述信息(就是打印了类的注释信息) print(Dog.__module__) # __module__属性是显示从哪个模块导入的 print(Dog.__class__) # __class__显示这个类本身的信息,从哪个模块,类名 print(Dog.__dict__) #__dict__按照字典的方式,把类的所有信息返回出来,不包括实例属性 d=Dog("laopeng") print(d.__dict__) # __dict__实例变量的所有信息,仅有实例属性 d("111",222,333,444,"hhhh") Dog("laopeng")() print(d) d["name"]="wangyue" d["job"]="IT" print(d["name"]) #python中的一切都是对象,所以呢 print(type(d)) #d是Dog的对象,Dog是一个类 print(type(Dog)) #Dog其实也是对象,是type的对象。。。。type()方法本身是内置的是用来查看实例类型的
什么情况下适用面向对象的设计思想:
1、多个函数具有共同参数时,可以将这些函数封装到一个类中做为成员方法,
用构造函数__init__()方法来传递相同参数,使用时直接用方法传个性参数就行了
2、根据一个模板创建某些东西的时候,使用类来做为模板是极好的
3、可能会反复使用,或者同样操作不同拆分组合的时候,可以把具体操作做为类的成员方法,使用时,各种组合就可以了。
其实基本上想写出个像样的东西,就不要考虑其他设计思想了,比如JAVA,人家都没有面向过程的语法。
定义类时self参数:就是实例自己,不是类自己。也就是调用封装在类中成员方法的主体,就是生成的那个具体实例
静态字段(共有属性),使用场景,所有实例都共有的属性,可以放在静态字段当中。
封装: 类中封装了静态字段和方法,对象中封装了普通字段(对象属性)的值
静态方法:就是定义一个函数,存在类中,使用起来很像普通函数
静态方法 + 类方法 + 属性方法
class Dog(object): head="tou" def __init__(self,name): self.name=name @staticmethod #静态方法,实际上跟类没啥关系了,就是一个单纯的函数。 # 只是名义上属于类,调用时,类名.函数名,实际用的话。。。比如 os模块的system()方法 mkdir()方法什么的。 #静态方法参与继承什么的 def eat(self,food): print("%s is eating %s"%(self.name,food)) @classmethod #类方法 def my_head(cls): print("my head is %s"%cls.head) @property #属性方法,外部不能传参,所以这里定义时不再定义形参,对外部来讲是属性,但会返回函数执行结果 def my_name(self): print(self.name+"gogogo") @my_name.setter #为一个属性方法提供修改的方法,这是为了像修改实例属性那样使用, def my_name(self,in_name): self.name=in_name print("set my_name:%s"%in_name) @my_name.deleter#为一个属性方法提供删除的方法,允许像删除实例属性那样使用 def my_name(self): del self.my_name d=Dog("laopeng") d.eat(d,"屎") d.my_head() d.my_name d.my_name="kkkkkkkkkk" d.my_name
静态方法只是名义上由类管理,实际上静态方法里无法访问类或者实例中的任何属性 实际应用的地方不多
类方法只能访问类变量,无法访问实例变量。实际应用的地方不多
属性方法,把一个方法变成一个静态属性(只读的),想要给他传值,需要在类中写一个同名方法,挂装饰@方法名.setter 在这个方法中进行相关赋值
属性方法实际上是隐藏了具体实现,对于外部应用 就是个属性
属性嘛,其实也希望进行修改和删除,那么就是需要再定义同名方法用装饰器@@my_name.setter @my_name.deleter即可实现。
当然了 外部搞定了,那内部终究还是方法,还可以做很多实现的。
我自己遭遇过的一次是定义了一个私有实例属性是个list,每个实例的变更都需要去修改它,
但是修改时还要进行加工,对外调方法又不爽的时候用的,其实调用方法也可以
metaclass:
class MyType(type): def __init__(self): print("mytype-init") def __call__(self, *args, **kwargs): print("mytype-call") self.__new__(self, *args, **kwargs) self.__init__(self, *args, **kwargs) class Foo(object): __metaclass__=MyType #定义一个元类,用来描述实例化的过程,1、元类call 2、元类new 3、元类new, # 元类中的self其实是指引用元类的类 def __init__(self): print("foo init") self.name="wy" def __new__(cls, *args, **kwargs): print("foo new") return object.__new__(cls, *args, **kwargs) f=Foo() print(f.name)
反射:
class Dog(object): def __init__(self,name): self.name=name def eat(self): print("%s is eating..."%self.name) #场景,先实例化一个狗,然后让用户去选择狗的行为方法 d=Dog("laopeng") choice=input("行为:") #d.choice choice是个字符串,不是方法名 if hasattr(d,choice): #hasattr()方法,参数一:实例,参数二:属性名,作用,判断实例中是否有这个属性 返回:True/False fun=getattr(d,choice)#getattr()方法,参数一:实例,参数二:属性名,作用,获取该属性的内存地址,返回:属性的内存地址。 fun() #最后方法拿到了,直接括号,就执行了,一般 拿到方法以后,写法上 另外定义一下变量,看着比较清楚 print(d.name) setattr(d,'name',"aliang") #直接给一个实例,装配设定一个属性,xyv三个参数,x实例,y属性名,v属性值,同理可以装配设定一个方法,setattr(d,'talk',bulk) print(d.name) delattr(d,'name') print(d.name) #直接删除实例的属性,参数一:实例,参数二:属性名