八、Python面向对象

(一)面向对象的概念


  • 历史背景:面向对象编程的产生源于对过程编程局限性的反思。过程编程在构造系统时,存在重用、维护和扩展的问题,且逻辑复杂,代码难以理解。因此,人们开始探索一种新的编程范式,即面向对象编程,试图通过模拟现实环境来设计应用程序。具体参看程序设计思想发展 - linhaifeng - 博客园
  • 应用领域:面向对象的概念和应用已经超越了程序设计和软件开发,扩展到了数据库系统、交互式界面、CAD技术、人工智能等多个领域。  

1、类

描述一类事物共同特征和行为的抽象概念,在Python中类是对象的模板,定义了一组变量(特征)和方法(行为)属性,可以用来创建具体的对象。

1. 站的角度不同,定义出的类是截然不同的,详见面向对象实战之需求分析

2. 现实中的类并不完全等于程序中的类,比如现实中的公司类,在程序中有时需要拆分成部门类,业务类...... 

3. 有时为了编程需求,程序中也可能会定义现实中不存在的类,比如策略类,现实中并不存在,但是在程序中却是一个很常见的类

4.python的class术语与c++有一定区别,与 Modula-3更像,python中一切皆为对象,且python3中类与类型是一个概念,类型就是类

#类型dict就是类dict
>>> list
<class 'list'>

#实例化的到3个对象l1,l2,l3
>>> l1=list()
>>> l2=list()
>>> l3=list()

#三个对象都有绑定方法append,是相同的功能,但内存地址不同
>>> l1.append
<built-in method append of list object at 0x10b482b48>
>>> l2.append
<built-in method append of list object at 0x10b482b88>
>>> l3.append
<built-in method append of list object at 0x10b482bc8>

#操作绑定方法l1.append(3),就是在往l1添加3,绝对不会将3添加到l2或l3
>>> l1.append(3)
>>> l1
[3]
>>> l2
[]
>>> l3
[]
#调用类list.append(l3,111)等同于l3.append(111)
>>> list.append(l3,111) #l3.append(111)
>>> l3
[111] 

2、对象

类的实例,描述某个具体事务的特征和行为,在Python面向对象编程中,对象是程序的基本单位,包含数据和操作这些数据的方法(实际是调用类的方法属性),现实中先有具体的物体,后有总结共同的特征和行为抽象出类的概念,但在程序中,必须先定义类,后产生对象。

# 现实中类和对象的例子:先有对象,再有类
"""
对象1:张三
    特征:
        学校=一中
        姓名=张三
        性别=男
        民族=汉
        年龄=18
    行为:
        学习
        吃饭
        睡觉

对象2:李四
    特征:
        学校=二中
        姓名=李四
        性别=女
        民族=汉
        年龄=15
    行为:
        学习
        吃饭
        睡觉

对象3:王五
    特征:
        学校=五中
        姓名=王五
        性别=男
        民族=汉
        年龄=13
    行为:
        学习
        吃饭
        睡觉


提炼对象的共同特征和行为,定义学生类
    相似的特征:
        学校
        姓名
        性别
        年龄
    共同的特征:
        民族 = 汉
    相似的行为:
        学习
        吃饭
        睡觉
"""
 # 程序中类和对象的例子:先定义类,后由类实例化对象
"""
1. 在程序中特征用变量标识,行为用方法(函数)标识
2. 因而类中最常见的无非是:变量和方法的定义
"""
# 定义类:
class Student:
    ming_zu = "汉"  # 类的数据属性,定义所有对象的共同特征
    # 除了共同的特征,还有相似但各个对象不尽相同的特征,这就用到了类的__init__构造方法
    #注意:__init__方法是在对象产生之后才会执行,只用来为对象进行初始化操作,可以有任意代码,但一定不能有返回值。(原理:实例化对象时,类名加()就会调用内置的__call__方法创建一个空对象(object类实例化的对象),然后自动调用__init__方法,并将创建的空对象作为参数传给该方法进行初始化,然后__call__方法返回这个初始化了的对象给要赋值的变量)
    def __init__(self, school, name, sex, age)
        self.school = school
        self.name = name
        self.sex = sex
        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.点是访问属性的语法,类中定义的名字,都是类的属性
"""
# 程序中类的用法
# .:专门用来访问属性,本质操作的就是__dict__
OldboyStudent.school #等于经典类的操作OldboyStudent.__dict__['school']
OldboyStudent.school='Oldboy' #等于经典类的操作OldboyStudent.__dict__['school']='Oldboy'
OldboyStudent.x=1  #等于经典类的操作OldboyStudent.__dict__['x']=1
del OldboyStudent.x  #等于经典类的操作OldboyStudent.__dict__.pop('x')

#程序中的对象
#调用类,或称为实例化,得到对象
s1=Student("一中", "张三", "男", 18)
s2=Student("二中", "李四", "女", 15)
s3=Student("五中", "王五", "男", 13)

#程序中对象的用法
#执行__init__,s1.name='张三',很明显也会产生对象的名称空间
s2.__dict__
{'school':'二中', 'name': '李四', 'age': '女', 'sex': 15}

s2.name  # s2.__dict__['name']

s2.name='史籍' # s2.__dict__['name']='史籍'
s2.course='python'  # s2.__dict__['course']='python'
del s2.course  # s2.__dict__.pop('course')


(二)面向对象设计


  • 面向对象的程序设计:核心是对象,更加注重对现实世界的模拟,是一种“上帝式”的思维方式,不像面向过程的程序设计(流水线式),可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法准确地预测最终结果,但解决了程序的扩展性,对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。
  • 面向对象的程序设计并不是全部。对于一个软件质量来说,还关注成本、性能、可靠性、安全性、可维护性、可移植性、可伸缩性,面向对象的程序设计只是用来解决扩展性,一些扩展性要求低的场景使用面向对象会徒增编程难度,比如管理linux系统的shell脚本就不适合用面向对象去设计,面向过程反而更加适合。
  • 应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方
  • 并不是只有class关键字定义类的方式才是面向对象的程序设计,用函数也可以实现面向对的程序设计,如下例子:
def dog(name, age, type): #定义一个狗的类
	def init(name, age, type): #定义一个初始化函数,用来实例化具体的狗对象,将它的特征和行为封装到字典里
		dog = {
			"name":name,
			"age":age,
			"type":type,
			"eat":eat
		}
		return dog
	def eat(dog): #定义行为函数
		print("狗%s在吃饭" % dog["name"])
	return init(name, age, type)  # 运行dog函数即初始化,并返回一个狗对象的字典
	
dog1 = dog("andy", 5, "藏獒") # 创建一个狗对象,即运行dog函数,获取具有狗对象特征和行为的字典
dog1["eat"](dog1) # 通过狗对象字典调用它的eat行为函数

(三)面向对象编程(OOP) 


  • 面向对象编程(OOP)是一种编程范式,它基于“万物皆对象”的理念,通过将现实世界中的事物抽象为计算机能够理解的模型,使得解决现实问题变得更加简单。
  • 在OOP中,程序被视为对象之间相互协作的过程,每个对象都具有自己的属性和方法,通过发送消息来告知彼此要做的事情。
  • 核心特性:封装、继承和多态。

  • 优点:数据封装、易于维护、重复使用代码。

  • 缺点:如果不恰当地应用OOP原则,可能导致过度设计和代码膨胀。

  • 未来发展趋势:OOP具有强大的生命力和广泛的适用性,未来可能会更加注重跨平台兼容性、并发编程和分布式系统的支持。 

class Dog: #类名首字母大写
	def __init__(self, name, age, type):
		self.name = name
		self.age =age
		self.type = type
	def eat(self):
		print("狗%s在吃饭" % self.name)
		
dog1 = Dog("andy", 5, "藏獒") # 类加括号,运行类就是实例化一个对象
dog.eat()

(四)类的相关知识


1、新式类和经典类  

  • python2才这么区分,python3中统一都是新式类(不管是否显式继承父类)。
  • 新式类和经典类声明的最大不同在于,所有类必须继承一个父类。
  • object类是所有类的父类,不管是否显式声明父类,都有一个默认继承的object类。
  • python2中经典类和新式类的区分:

        经典类:
        class 类名:
            pass
        新式类:
        class 类名(父类):
            pass

  • 注:python3中这两种方式定义都是新式类,python2中继承经典类的类也是经典类,新式类要顶层父类显式继承object类。

2、类的属性 

所有这类对象共有的属性,分为数据属性和函数属性
2.1)数据属性:变量,即类的特征,所有对象共享,id都一样,如dang="共产党"

print(id(Student.ming_zu))

print(id(s1.ming_zu))
print(id(s2.ming_zu))
print(id(s3.ming_zu))

'''
4377347328
4377347328
4377347328
4377347328
'''

2.2)函数属性:函数,即类的行为,如def eat()或def shuijiao(self),命名规则动词_名词,绑定给对象用,obj.method称为绑定方法,虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法,内存地址都不一样,绑定给谁就由谁来调用,谁来调用,就会将‘谁’本身当做第一个参数传给方法,即自动传值(方法__init__也是一样的道理)

print(Student.learn)
print(s1.learn)  # 等同于Student.learn(s1)
print(s2.learn)  # 等同于Student.learn(s2)
print(s3.learn)  # 等同于Student.learn(s3)
# 注意:绑定到对象的方法的这种自动传值的特征,决定了在类中定义的函数都要默认写一个参数self,self可以是任意名字,但是约定俗成地写出self。
'''
<function Student.learn at 0x1021329d8>
<bound method Student.learn of <__main__.Student object at 0x1021466d8>>
<bound method Student.learn of <__main__.Student object at 0x102146710>>
<bound method Student.learn of <__main__.Student object at 0x102146748>>
'''
#ps:id是python的实现机制,并不能真实反映内存地址,如果有内存地址,还是以内存地址为准

2.3)类和对象都用点.来调用自己的属性,如Chinese.dong
2.4)dir(类名):查看类的属性列表(只存放属性名)
2.5)类名.__dict__:查看类的属性字典(属性名:属性值,用.调用属性实质上就是在这个属性字典里找该属性对应的值)
2.6)类的内置属性:
__name__:类的名字(字符串)
__doc__:类的文档字符串
__base__:类的第一个父类
__bases__:类的所有父类构成的元组
__dict__:类的属性字典
__module__:类定义所在的模块,如果就在执行文件,值为__main__
__class__:实例对应的类(仅新式类中)
__init__:类的初始化函数,实例化自动调用
__mro__:经典类没有这个属性,新式类的继承顺序MRO元组(定义每个类时python会根据C3线性算法构造出一个方法解析顺序(MRO)元组,就是一个简单的所有基类的线性顺序元组,遵循三条准则:a、子类先于父类被检查;b、多个父类会根据它们在元组中的顺序被检查;c、如果对于下一个类有两个合法的选择,选择第一个,即找到后停止寻找)
2.7)不能直接修改类的属性字典,会导致oop(面向对象)不稳定
2.8)类属性又称为静态变量,或静态数据,与所属的类对象绑定,不依赖任何类实例,相当于Java和c++中在一个变量声明前加static关键字
2.9)修改属性:类名.属性名=新的值
2.10)增加属性:类名.新的属性名=值
2.11)删除属性:del 类名.属性名
2.12)不是.调用的变量(包括在类函数中),不会找类属性和实例属性,只会找普通变量
2.13)是.调用的变量(包括在类函数中),只会找类属性和实例属性,不会找普通变量 

 3、面向对象之绑定方法与非绑定方法

3.1 类中定义的函数分类

一:绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入):

    1. 绑定到类的方法:用classmethod装饰器装饰的方法。

                为类量身定制

                类.boud_method(),自动将类当作第一个参数传入

              (其实对象也可调用,但仍将类当作第一个参数传入)

    2. 绑定到对象的方法:没有被任何装饰器装饰的方法。

               为对象量身定制

               对象.boud_method(),自动将对象当作第一个参数传入

             (属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说)

二:非绑定方法:用staticmethod装饰器装饰的方法

        1. 不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。就是一个普通工具而已

    注意:与绑定到对象方法区分开,在类中直接定义的函数,没有被任何装饰器装饰的,都是绑定到对象的方法,可不是普通函数,对象调用该方法会自动传值,而staticmethod装饰的方法,不管谁来调用,都没有自动传值一说

3.2 绑定方法 

  • 绑定给对象的方法: 没有任何装饰器修饰的方法,通常由实例化的对象调用。
  • 绑定给类的方法:@classmethod装饰的方法,即类方法,通常由类本身调用。
    import settings
    class MySQL:
        def __init__(self,host,port):  # 绑定到对象的方法
            self.host=host
            self.port=port
    
        @classmethod
        def from_conf(cls):  # 类方法-绑定到类的方法
            print(cls)
            return cls(settings.HOST,settings.PORT)
    
    print(MySQL.from_conf) #<bound method MySQL.from_conf of <class '__main__.MySQL'>>
    conn=MySQL.from_conf()
    
    conn.from_conf() #对象也可以调用,但是默认传的第一个参数仍然是类

3.3 非绑定方法

  • 在类内部用staticmethod装饰的函数即非绑定方法,就是普通函数
  • statimethod不与类或对象绑定,谁都可以调用,没有自动传值效果
import hashlib
import time
class MySQL:
    def __init__(self,host,port):
        self.id=self.create_id()
        self.host=host
        self.port=port
    @staticmethod
    def create_id(): #就是一个普通工具
        m=hashlib.md5(str(time.time()).encode('utf-8'))
        return m.hexdigest()


print(MySQL.create_id) #<function MySQL.create_id at 0x0000000001E6B9D8> #查看结果为普通函数
conn=MySQL('127.0.0.1',3306)
print(conn.create_id) #<function MySQL.create_id at 0x00000000026FB9D8> #查看结果为普通函数

 3.4 classmethod和staticmethod 

classmethod是类方法,staticmethod是静态方法,如果修饰同名方法,通过类名调用会调用classmethod装饰的方法。

host = "127.0.0.1"
port = 8080

class MySQL:
    def __init__(self,host,port):
        self.host=host
        self.port=port

    @staticmethod
    def from_conf():
        return MySQL(host,port)

    # @classmethod #哪个类来调用,就将哪个类当做第一个参数传入
    # def from_conf(cls):
    #     return cls(host,port)

    def __str__(self):
        return '就不告诉你'

class Mariadb(MySQL):
    def __str__(self):
        return '<%s:%s>' %(self.host,self.port)


m=Mariadb.from_conf()
print(m) #我们的意图是想触发Mariadb.__str__,但是结果触发了MySQL.__str__的执行,打印就不告诉你,因为m是返回的MySQL类的实例

# 放开classmethod装饰的from_conf(cls)方法,就不会触发staticmethod装饰的from_conf()方法了,结果就是我们要的<127.0.0.1:8080>

3.5 练习 

定义MySQL类

  1.对象有id、host、port三个属性

  2.定义工具create_id,在实例化时为每个对象随机生成id,保证id唯一

  3.提供两种实例化方式,方式一:用户传入host和port 方式二:从配置文件中读取host和port进行实例化

  4.为对象定制方法,save和get_obj_by_id,save能自动将对象序列化到文件中,文件路径为配置文件中DB_PATH,文件名为id号,保存之前验证对象是否已经存在,若存在则抛出异常,;get_obj_by_id方法用来从文件中反序列化出对象

(1)创建唯一id之UUID
原文链接:http://www.cnblogs.com/dkblog/archive/2011/10/10/2205200.html
 Python官方Doc:《20.15. uuid — UUID objects according to RFC 4122》
    UUID的算法介绍:《A Universally Unique IDentifier (UUID) URN Namespace》

概述:

    UUID是128位的全局唯一标识符,通常由32字节的字符串表示。
    它可以保证时间和空间的唯一性,也称为GUID,全称为:
            UUID —— Universally Unique IDentifier      Python 中叫 UUID
            GUID —— Globally Unique IDentifier          C#  中叫 GUID

    它通过MAC地址、时间戳、命名空间、随机数、伪随机数来保证生成ID的唯一性。
    UUID主要有五个算法,也就是五种方法来实现:

       1、uuid1()——基于时间戳

               由MAC地址、当前时间戳、随机数生成。可以保证全球范围内的唯一性,
               但MAC的使用同时带来安全性问题,局域网中可以使用IP来代替MAC。

       2、uuid2()——基于分布式计算环境DCE(Python中没有这个函数)

                算法与uuid1相同,不同的是把时间戳的前4位置换为POSIX的UID。
                实际中很少用到该方法。

      3、uuid3()——基于名字的MD5散列值

                通过计算名字和命名空间的MD5散列值得到,保证了同一命名空间中不同名字的唯一性,
                和不同命名空间的唯一性,但同一命名空间的同一名字生成相同的uuid。    

       4、uuid4()——基于随机数

                由伪随机数得到,有一定的重复概率,该概率可以计算出来。

       5、uuid5()——基于名字的SHA-1散列值

                算法与uuid3相同,不同的是使用 Secure Hash Algorithm 1 算法

使用方面:

    首先,Python中没有基于DCE的,所以uuid2可以忽略;
    其次,uuid4存在概率性重复,由无映射性,最好不用;
    再次,若在Global的分布式计算环境下,最好用uuid1;
    最后,若有名字的唯一性要求,最好用uuid3或uuid5。

编码方法:

    # -*- coding: utf-8 -*-

    import uuid

    name = "test_name"
    namespace = "test_namespace"

    print uuid.uuid1()  # 带参的方法参见Python Doc
    print uuid.uuid3(namespace, name)
    print uuid.uuid4()
    print uuid.uuid5(namespace, name)
 (2)练习代码
(2.1)settings.py
HOST='127.0.0.1'
PORT=3306
DB_PATH=r'E:\CMS\aaa\db'
(2.2)test.py 
import settings
import uuid
import pickle
import os
class MySQL:
    def __init__(self,host,port):
        self.id=self.create_id()
        self.host=host
        self.port=port

    def save(self):
        if not self.is_exists:
            raise PermissionError('对象已存在')
        file_path=r'%s%s%s' %(settings.DB_PATH,os.sep,self.id)
        pickle.dump(self,open(file_path,'wb'))

    @property
    def is_exists(self):
        tag=True
        files=os.listdir(settings.DB_PATH)
        for file in files:
            file_abspath=r'%s%s%s' %(settings.DB_PATH,os.sep,file)
            obj=pickle.load(open(file_abspath,'rb'))
            if self.host == obj.host and self.port == obj.port:
                tag=False
                break
        return tag
    @staticmethod
    def get_obj_by_id(id):
        file_abspath = r'%s%s%s' % (settings.DB_PATH, os.sep, id)
        return pickle.load(open(file_abspath,'rb'))

    @staticmethod
    def create_id():
        return str(uuid.uuid1())

    @classmethod
    def from_conf(cls):
        print(cls)
        return cls(settings.HOST,settings.PORT)

# print(MySQL.from_conf) #<bound method MySQL.from_conf of <class '__main__.MySQL'>>
conn=MySQL.from_conf()
conn.save()

conn1=MySQL('127.0.0.1',3306)
conn1.save() #抛出异常PermissionError: 对象已存在


obj=MySQL.get_obj_by_id('7e6c5ec0-7e9f-11e7-9acc-408d5c2f84ca')
print(obj.host)

 3.6 其他练习

class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    @staticmethod
    def now(): #用Date.now()的形式去产生实例,该实例用的是当前时间
        t=time.localtime() #获取结构化的时间格式
        return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回
    @staticmethod
    def tomorrow():#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间
        t=time.localtime(time.time()+86400)
        return Date(t.tm_year,t.tm_mon,t.tm_mday)

a=Date('1987',11,27) #自己定义时间
b=Date.now() #采用当前时间
c=Date.tomorrow() #采用明天的时间

print(a.year,a.month,a.day)
print(b.year,b.month,b.day)
print(c.year,c.month,c.day)


#分割线==============================
import time
class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    @staticmethod
    def now():
        t=time.localtime()
        return Date(t.tm_year,t.tm_mon,t.tm_mday)

class EuroDate(Date):
    def __str__(self):
        return 'year:%s month:%s day:%s' %(self.year,self.month,self.day)

e=EuroDate.now()
print(e) #我们的意图是想触发EuroDate.__str__,但是结果为
'''
输出结果:
<__main__.Date object at 0x1013f9d68>
'''
因为e就是用Date类产生的,所以根本不会触发EuroDate.__str__,解决方法就是用classmethod

import time
class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    # @staticmethod
    # def now():
    #     t=time.localtime()
    #     return Date(t.tm_year,t.tm_mon,t.tm_mday)

    @classmethod #改成类方法
    def now(cls):
        t=time.localtime()
        return cls(t.tm_year,t.tm_mon,t.tm_mday) #哪个类来调用,即用哪个类cls来实例化

class EuroDate(Date):
    def __str__(self):
        return 'year:%s month:%s day:%s' %(self.year,self.month,self.day)

e=EuroDate.now()
print(e) #我们的意图是想触发EuroDate.__str__,此时e就是由EuroDate产生的,所以会如我们所愿
'''
输出结果:
year:2017 month:3 day:3
'''

(五)对象的相关知识


 1、实例化

运行类的__init__初始化函数,self就是实例本身,产生一个对象的属性字典

p = Chinses()  # 会将对象名p传递给Chinses类的__init_构造方法_的self参数
# 实际上是调用了Chinses类的__call__方法,该方法自动调用__init__方法
# 原理:实例化对象时,类名加()就会调用内置的__call__方法创建一个空对象(object类实例化的对象),然后自动调用__init__方法,并将创建的空对象作为参数传给该方法进行初始化,然后__call__方法返回这个初始化了的对象给要赋值的变量

2、对象的属性 

实例属性
2.1)对象只有数据属性:变量,即对象的特征,如self.name=值
2.2)对象可以调用类的数据属性和函数属性(会自动将对象本身传递给该函数的第一个位置参数,一般是self),但类不能访问对象的属性,因为对象的作用域就是init函数的作用域,对象调用属性时先在自己的属性字典里面找,如果找不到再到类的属性字典里面找,如果再找不到报错
2.3)用点.来调用自己的属性,如c.name
2.4)dir(对象名):查看对象的属性列表(只存放属性名)
2.5)对象名.__dict__:查看对象的属性字典(属性名:属性值,用.调用属性实质上就是在这个属性字典里找该属性对应的值)
2.6)对象的内置属性:
__dict__:对象的属性字典
__class__:实例对应的类(仅新式类中)
2.7)不能直接修改对象的属性字典,会导致oop不稳定
2.8)修改数据属性:对象名.属性名=新的值
2.9)增加数据属性:对象名.新的属性名=值,对象只有调用类的函数属性时会自动传递对象给self,所以如果对象增加函数属性,传参时需要手动传递对象
2.10)删除数据属性:del 对象名.属性名
2.11)不是.调用的变量(包括在类函数中),不会找类属性和实例属性,只会找普通变量
2.12)是.调用的变量(包括在类函数中),只会找类属性和实例属性,不会找普通变量
2.13)对象调用类属性,如列表变量,通过apeend增加元素,修改的是类属性的值,通过赋值修改值,才是新增或修改实例属性 

3、对象之间的交互 

class Garen:        #定义英雄盖伦的类,不同的玩家可以用它实例出自己英雄;
    camp='Demacia'  #所有玩家的英雄(盖伦)的阵营都是Demacia;
    def __init__(self,nickname,aggressivity=58,life_value=455): #英雄的初始攻击力58...;
        self.nickname=nickname  #为自己的盖伦起个别名;
        self.aggressivity=aggressivity #英雄都有自己的攻击力;
        self.life_value=life_value #英雄都有自己的生命值;
    def attack(self,enemy):   #普通攻击技能,enemy是敌人;
        enemy.life_value-=self.aggressivity #根据自己的攻击力,攻击敌人就减掉敌人的生命值。

class Riven:
    camp='
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值