class类
------------以下截图摘自:马哥教育
面向对象:
什么是面向对象呢?
一种认识世界,分析世界的方法论。将万事万物抽象为类。
举例:
你吃鱼
你,就是对象;鱼,也是对象;吃简单理解就是动作。
你是具体的人,是具体的对象。你属于人类,人类是抽象的概念,是无数具体的个体的抽象。
鱼🐠,也是具体的的对象,就是你吃的这一天具体的鱼。这条鱼属于鱼类,是无数的鱼的抽象出来的概念。
在学习编程之前,我们日常生活中想要更好的了解面向对象,就要先时刻注意面向对象的特点:
1.特征;
2.行为;
3.关系。
但是,如果按照这个思路的话,我们在分析一个对象的时候,会发现,这个对象有很多的特征和行为,没有标准的答案。
那么,我们的设计工作怎么才能算是好的分析设计呢?答案就是:
该有的东西有;不该有的就没有或者忽略。
实际工作中,我们的工作过程是:
得到需求:
1.分析对象:
1>.特征 例如:颜色;品牌;尺寸......
2>.行为 例如:开 ; 关......
2.设计(用代码体现出来分析的思路和结果):写类 ClassName():
特征:字段/属性 例如:price = xxx
例如:size = xxx
......
行为:方法(函数)例如:def on():
例如:def off():
......
3.实例化(映射到具体的实例):
例如:c1 = ClassName(('xxx')
例如:c2 = ClassName(('yyy')
......
无论用什么形式来编程,我们都要明确记住以下原则:
1.写重复代码是非常不好的低级行为
2.你写的代码需要经常变更
开发正规的程序跟那种写个运行一次就扔了的小脚本一个很大不同就是,你的代码总是需要不断的更改,不是修改bug就是添加新功能等,所以为了日后方便程序的修改及扩展,你写的代码一定要循序易读、易改的原则(专业数据叫可读性好、易扩展)
如果你把一段同样的代码复制、粘贴到了程序的多个地方以实现在程序的各个地方调用这个功能,那么,日后你再对这个功能进行修改时,就需要把程序里多个地方都改一遍,这种写程序的方式是有问题的,因为如果你不小心漏掉了一个地方没改,那可能会导致整个程序的运行都出问题。因此我们知道开发中一定要努力避免写重复的代码,否则就相当于自己再挖坑。
还好,函数的出现就能帮我们轻松的解决重复代码的问题,对于需要重复调用的功能,只需要把它写成一个函数,然后在程序的各个地方直接调用这个函数名就好了,并且当需要修改这个功能的时候,只需改函数代码,然后整个程序就都更新了。
类是一个抽象的概念,每个人都有自己的理解。我自己的理解是,你设计一个作品时,要从宏观到微观,从大到小,把框架性的东西先设计出来,而框架下面具体的个性化的东西里面有共同属性的东西可以首先单独设计出来,归为一类,再设计个性化的东西时,只需要设计那些个性化的属性/数据(参数),就能节省很多时间和精力,还可以随时根据需要添加个性化东西的数量和种类。
类class
类是抽象的概念,是万事万物的抽象,是一类事物的共同特征的集合。
用计算机语言来描述类,就是属性和方法的集合。
对象instance、object
对象是类的具象,是一个实体(实例)。
对于我们每个人这个个体,都是抽象概念人类的不同实体。
定义:
class ClassName: #语句块
1.必须使用class关键字;
2.类名必须是用大驼峰命名,最起码首字母必须大写;
3.类定义完成后,就产生了一个类对象,绑定到了ClassName上。
class MyClass: x = 123 # 类属性,也是类变量。 def __init__(self): # 初始化,在实例化的过程中是最早执行的 print("init") def foo(self): # 类属性foo,也是方法 print(id(self)) return "foo = {}".format(self.x) mycls = MyClass() # 实例化,初始化(会默认调用__init__) print(mycls.x) print(mycls.foo())
类对象及类属性
类对象:类的定义就会产生 一个类对象;
类的属性:类定义中的变量和类中定义的方法都是类的属性
类变量: x是类MyClass的变量。
foo方法是类的属性,如同吃是人类的方法,但是每一个具体的人才能吃东西,也就是说吃是人的实例才能调用的方法,
foo是method方法对象,不是普通的函数对象function了,它必须至少有一个参数,且第一个参数必须是self(self可以换名字,但是不要换),这个参数位置就留给了self。
self指代当前实例本身
构造函数(构造方法)
__init__(self, 参数): 初始化函数,当类被实例化后就默认调用了__init__,其中被实例化的对象就默认传入了self,所以self就是实例本身;参数因为保存在了实例对象上,也就成了实例变量。
self.参数 = 参数 其中self.参数 就是对象(实例)的属性
构造函数实际意义:对实例进行初始化。
注意:__init__(self): 方法不能有返回值,也就是只能是None
为了简单理解类,我们举个简单的小例子:
class MyClass: def __init__(self): print("self in init = {}".format(id(self))) c = MyClass() # 默认调用__init__ print("c = {}".format(id(c)))
结果:
上面证明实例c和初始化函数__init__中的self指向同一个内存地址。
实例变量是每一个实例自己的变量,是自己独有的;
类变量是类的变量,是类的所有实例共享的属性和方法。
特殊属性 | 含义 |
__name__ | 对象名 |
__class__ | 对象的类型 |
__dict__ | 对象的属性的字典 |
__qualname__ | 类的限定名 |
总结
是类的,也是这个类所有实例的,其实例都可以访问到;是实例的,就是这个实例自己的,通过类访问不到。
实例可以动态的给自己增加属性。实例.__dict__[变量名] 和 实例.变量名 都可以访问到。
实例的同名变量会隐藏这类变量,或者说是覆盖了这个类变量。
实例属性的查找顺序
指的是实例使用 . 来访问属性,会先找自己的__dict__,如果没有,然后通过属性__class__找到自己的类,再去类的__dict__中找;
注意,如果实例使用__dict__[变量名]访问变量,将不会按照上面的查找顺序找变量了。
一般来说,类变量使用全大写来命名。
class MyClass: x = 123 def foo(self): print("foo") @classmethod # 类方法,跟类相关 def clsmethod(cls): print("{}.x is {}".format(cls.__name__, cls.x)) @staticmethod # 静态方法,只是归类管理。 def staticmtd(): print("staticmethod") a = MyClass() # 实例化,初始化 a.foo() MyClass.clsmethod() a.clsmethod() # 相当于 a.__class__.clsmethod MyClass.staticmtd() a.staticmtd()
类方法和静态方法
前面的例子中定义的__init__等方法,这些方法本身都是类的属性,第一个参数必须是self,而self必须指向一个对象,也就是类必须实例化之后,由实例来调用这个方法。
类方法
1.在类定义中,使用@classmethod装饰器修饰的方法;
2.必须至少有一个参数,且第一个参数留给了cls,cls值代调用者即类对象本身;
3.cls这个标识符可以是任何合法名称,但是为了易读,请不要修改;
4.通过cls可以直接操作类的属性。注意:无法通过cls操作类的实例
静态方法
1.在类定义中,使用@staticmethod装饰器修饰的方法;
2.调用时,不会隐式传入参数;
静态方法,知识标明这个方法属于这个名词空间。函数归在一起,方便组织管理。
object对象
一个对象既是一个类实例化后的实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同。
面向对象编程的特性:
Encapsulation 封装
在类中对数据的赋值,内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法,在实际应用中就是把类中的属性和方法变成私有。
私有(Private)属性
使用双划线开头的属性名,就是私有属性.
举例:
class Person: def __init__(self, name, age=18): self.name = name self.__age = age # 通过给属性名前面加__, __age变成私有实例变量(私有属性) def growup(self, incr=1): if 0 < incr < 150: self.__age += incr def getage(self): return self.__age tom = Person("Tom") tom.growup(2) print(tom.getage()) # 通过内部方法可以访问到私有属性,实际开发中尽量使用内部方法。 ################################################################################# print(Person.__dict__) # 在类的字典中找不到关于age的信息 print(tom.__dict__) # 在实例的字典中找到了变了属性名的age信息 tom._Person__age = 200 # 这种方法破坏封装,实际开发中不要使用 print(tom.getage()) # 一旦找到新的私有属性名,还是可以更改私有属性 tom.age = 300 # 这种方法也破坏封装,实际开发中不要使用 print(tom.getage()) print(tom.age) print(tom.__dict__) # 通过实例字典我们可以看到当属性名不一样时,tom.age=300相当于给实例增加了一个实例变量。
私有变量的本质
类定义的时候,如果声明一个实例变量的时候,使用双下划线,python解释器会将其改名,转换名为:_类名__变量名 的名称,所以用原来的名字访问不到了。
保护变量
在变量名前使用一个下划线,成为保护变量。
保护变量根本就没有改变属性名称,和普通属性一样。解释器不做任何特殊处理。是程序开发者共同的约定,可以看作是一个提醒或警示。看到这种变量,就如同私有变量,不要直接使用。
私有方法
使用双下划线的方法,就是私有方法。
私有方法的本质
单下划线的方法只是开发者之间的约定,解释器不做任何改变。
双下划线的方法是私有方法,解释器会改名,改名策略和私有变量相同,_类名__方法名。
方法变量都在类的__dict__中可以找到。
私有成员总结
在Python中使用_单下划线或者__双下划线来标识一个成员被保护或者被私有化隐藏起来。
但是,不管使用什么样的访问控制,都不能真正的阻止用户修改类的成员。Python中没有绝对的安全保护成员或者私有成员。
因此,前导的下划线只是一种警告或者提醒,请遵守这个约定。除非真有必要,不要修改或者使用保护成员或者私有成员,更不要修改它们。
Inheritance 继承
面向对象编程(OOP)语言的一个主要功能就是“继承“。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类“或者“派生类”。
被继承的类称为“基类”、“父类”或者“超类”。
继承的过程,就是从一般到特殊的过程。
要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
在某些OOP语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
继承概念的实现方式有三类;实现继承、接口继承和可视继承。
1.实现继承是指使用基类的属性和方法而无需额外编码的能力;
2.接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;
3.可视继承是指子窗体(类)使用基窗体(类)的外观和实现代码的能力。
在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如:二壮是一个人,春香也是一个人,因此这两个类都可以继承人类,但是Leg类却不能继承人类,因为Leg并不是一个人。
抽象类仅定义将由子类创建的一般属性和方法。
OOP开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成)→用类与实例进行设计和实现几个阶段。
一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承,经典类和新式类最大的继承区别就是:python2:新式类是广度优先原则,经典类是深度优先原则。python3:经典类和新式类统一按照广度优先的原则
class People(object): # 新式类 def __init__(self, name, age): self.name = name self.age = age self.friends = [] def eat(self): print("%s 在吃。。。" % self.name) def talk(self): print("%s 在说话。。。" % self.name) def sleep(self): print("%s 在睡觉。。。" % self.name) class sociality(object): def make_friends(self, obj): print("%s 和 %s 交朋友" % (self.name, obj.name)) self.friends.append(obj) # obj也是可以实例化的参数 class Man(People, sociality): # 类名()内的参数传人父类的类名就是继承 def __init__(self, name, age, money): # 重构父类函数时,要把()内参数全部传过来 People.__init__(self, name, age) # 经典类继承方法:继承父类构造函数,不用重复写父类那样的代码 super(Man, self).__init__(name, age) # 新式类的继承方法:还可以用内置函数super来继承父类构造函数, # 结果同上,但是更方便,不用担心父类名更改 self.money = money # 增加新的变量赋值 print("%s 一出生就有 %s 块钱" % (self.name, self.money)) def sleep(self): # 子类可以增加父类函数的功能,重构父类 People.sleep(self) # 可以直接调用父类里的函数 print("Man 在睡觉。。。") class Woman(People, sociality): # 子类之间不可以互相调用函数,但是可以多继承 def bear(self): print("%s 能够生小孩" % self.name) m1 = Man("二壮", 18, 10) # 子类重构 构造函数时,()内传参数需要和重构后的参数数量一致 m1.eat() m1.sleep() w1 = Woman("春香", 20) w1.bear() m1.make_friends(w1) print(m1.friends[0].name)
Polymorphism 多态
多态性(Polymorphisn)是允许你将父对象设置成为和一个或者更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
那么,多态的作用是什么呢?我们知道,封装可以隐藏现实细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了--代码重用。而多态则是为了实现另一个目的--接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性的正确调用。
python不直接支持多态,但可以间接实现
多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
编程其实就是一个将具体世界进行世界进行抽象化的过程,多态就是抽象化的一种体现,把一个系列具体事物的共同特点抽象出来,再通过这个抽象的事物,与不同的具体事物进行对话。
对不同类的对象发出相同的消息将会有不同的行为。比如,你的老板让所有员工在九点钟开始工作,他只要在九点钟的时候说:“开始工作”即可,而不要要对销售人员说:“开始销售工作”,对技术人员说:“开始技术工作”。因为“员工”是一个抽象的事物,只要是员工就可以开始工作,他知道这一点就行了。至于每个员工,当然会各司其职,做各自的工作。
多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定。
class Animal: def __init__(self, name): self.name = name def talk(self): raise NotImplementedError @staticmethod # 静态类方法(装饰器) def animal_talk(self): # 可以设置一个接口函数,间接实现多态 self.talk() class Cat(Animal): def talk(self): print("喵!喵!喵") class Dog(Animal): def talk(self): print("汪!汪!汪") d = Dog("二汪") c = Cat("小白") Animal.animal_talk(d) # 不用再去用d.talk()调用函数,统一用Animal.animal_talk(变量名)这个接口来实现调用 Animal.animal_talk(c)
实例化就是开辟一个内存,把变量名也作为一个参数传人内存中,调用的过程就是调用类中的函数,变量名就是作为参数赋值给了各个函数里面的self参数
我们继续设计一个类似于CS的游戏程序,思路如下:
class Role(object): def __init__(self, name, role, weapon, life_value=100, money=10000): # 构造函数,self是给变量名赋值准备的参数(r1=self) # 作用:在实例化时做一些类的初始化工作 self.name = name # 实例变量(静态属性),作用域是实例本身 self.role = role self.weapon = weapon self.__life_value = life_value # __life_value:私有属性(隐藏属性,无法直接访问) self.money = money def __del__(self): # 析构函数 print("%s 彻底死了。。。" % self.name) def show_status(self): # 显示各个属性 print("name:%s weapon:%s life_value:%s" % (self.name , self.weapon , self.__life_value)) def __shot(self): # 类的方法,功能(动态属性),__shot:私有方法,外部无法访问 print("shoting...") def got_shot(self): print("%s:ah...,i got shot... " % self.name) def buy_gun(self, gun_name): print("%s just bought %s " % (self.name, gun_name)) name = "我是类" # 类变量,和实例变量的区别就是优先级不同,具体执行是:先调用实例变量,如果没有再调用类变量 n = 123 n_list = [] print(Role.name, Role.n) r1 = Role("Alex", "police", "AK47") # 把类变成一个具体对象的过程叫做实例化(初始化一个类,造了一个对象) r1.name = "大壮" # 实例变量可以更改 r1.ballproof_clothes = True # 实例变量可以增加,但只对本实例有影响 r1.n_list.append("from r1") # 实例里面用.append添加参数,会改变类变量里面的参数 r1.n = "改类变量" # 在实例里面给类变量赋值,和类变量没有关系,相当于在实例里面新生成了一个变量n=“改类变量” del r1.weapon # 实例变量可以删除 print(r1.name, r1.n, r1.ballproof_clothes, r1.weapon) # 实例变量调用.name,.n 先调用实例变量,.name有参数Alex,就调用,.n没有参数,就去类里面调用参数123 r2 = Role("Jack", "terrorist", "喷子") # 生成角色 r2.n_list.append("from r2") Role.n = "ABC" # 类变量可以用类调用方法改 print(r1.n, r2.n, r2.n_list) r1.buy_gun("AK47") # 调用买枪函数 r2.got_shot() # 实际执行的过程是:Role.got_shot(r2)
析构函数:在实例释放、销毁的时候执行的,通常用于做一些收尾工作,如:关闭一些数据库链接打开的临时文件
我自己编辑了个剪刀,石头,布的游戏,如下:
V1.0版本:凑合用用类
import random class judge(object): auto_random = ["石头", "布", "剪刀"] auto_attack = random.choice(auto_random) def __cmp__(self, other): # 比较运算需要用到__cmp__函数 if self == "石头" and other == "石头": print("别人出了:%s 所以平局!" % other) elif self == "石头" and other == "剪刀": print("别人出了:%s 所以你赢啦!" % other) elif self == "石头" and other == "布": print("别人出了:%s 所以你输了!" % other) elif self == "布" and other == "布": print("别人出了:%s 所以平局!" % other) elif self == "布" and other == "剪刀": print("别人出了:%s 所以你输啦!" % other) elif self == "布" and other == "石头": print("别人出了:%s 所以你赢啦!" % other) elif self == "剪刀" and other == "剪刀": print("别人出了:%s 所以平局!" % other) elif self == "剪刀" and other == "石头": print("别人出了:%s 所以你输啦!" % other) elif self == "剪刀" and other == "布": print("别人出了:%s 所以你赢啦!" % other) else: print("別闹了,瞎出!") player_me = input("請出: ") auto_attack = judge.auto_attack judge.__cmp__(player_me, auto_attack)
然后可以扩展思路,分门别类,规划出玩家成为一个类。
就有了V2.0版本:
import random class player(object): def __init__(self, name, attack): self.name = name self.attack = attack def __del__(self): print("%s Game Over!" % self.name) def stone(self): print("%s出:石头" % self.name) self.attack = "石头" return self.attack def scissors(self): print("%s出:剪刀" % self.name) self.attack = "剪刀" return self.attack def cloth(self): print("%s出:布" % self.name) self.attack = "布" return self.attack AUTO_RANDOM = ["石头", "布", "剪刀"] AUTO_ATTACK = random.choice(AUTO_RANDOM) class judge(object): def __cmp__(self, other): # 比较运算需要用到__cmp__函数 if self == "石头" and other == "石头": print("别人出了:%s 所以平局!" % other) elif self == "石头" and other == "剪刀": print("别人出了:%s 所以你赢啦!" % other) elif self == "石头" and other == "布": print("别人出了:%s 所以你输了!" % other) elif self == "布" and other == "布": print("别人出了:%s 所以平局!" % other) elif self == "布" and other == "剪刀": print("别人出了:%s 所以你输啦!" % other) elif self == "布" and other == "石头": print("别人出了:%s 所以你赢啦!" % other) elif self == "剪刀" and other == "剪刀": print("别人出了:%s 所以平局!" % other) elif self == "剪刀" and other == "石头": print("别人出了:%s 所以你输啦!" % other) elif self == "剪刀" and other == "布": print("别人出了:%s 所以你赢啦!" % other) else: print("別闹了,瞎出!") player_me = player("Jack", "出") auto_attack = player.AUTO_ATTACK judge.__cmp__(player_me.stone(), auto_attack) del player_me
经过同学指点,简化了内容,增加了可玩性,于是,出了V3.0版本:
import random class player(object): def __init__(self, name, attack="出"): self.name = name self.attack = attack def __del__(self): print("{0} Game Over!".format(self.name)) def show(self): if self.name == "computer": self.attack = random.choice(["石头", "布", "剪刀"]) else: self.attack = input("請{0}出:".format(self.name)) print("{0}出:{1}".format(self.name, self.attack)) return self.attack class judge(object): def __cmp__(self, other): # 比较运算需要用到__cmp__函数 if self == "石头" and other == "石头": print("别人出了:{0} 所以平局!".format(other)) elif self == "石头" and other == "剪刀": print("别人出了:{0} 所以你赢啦!".format(other)) elif self == "石头" and other == "布": print("别人出了:{0} 所以你输了!".format(other)) elif self == "布" and other == "布": print("别人出了:{0} 所以平局!".format(other)) elif self == "布" and other == "剪刀": print("别人出了:{0} 所以你输啦!".format(other)) elif self == "布" and other == "石头": print("别人出了:{0} 所以你赢啦!".format(other)) elif self == "剪刀" and other == "剪刀": print("别人出了:{0} 所以平局!".format(other)) elif self == "剪刀" and other == "石头": print("别人出了:{0} 所以你输啦!".format(other)) elif self == "剪刀" and other == "布": print("别人出了:{0} 所以你赢啦!".format(other)) else: print("別闹了,瞎出!") if __name__ == '__main__': while True: player_me = player("Jack") player_computer = player("computer") judge.__cmp__(player_me.show(), player_computer.show()) break del player_me