class Student:
def __init__(self, name, height, weigth, money,likes):
self.name = name
self.height = height
self.weigth = weigth
self.money = money
self.likes = likes
# self.likes = "睡觉" # 没有通过参数传入的,而是设置一个默认值,表示只要实例化对象,内部就会有一个爱好:睡觉
# 即是 人人都爱睡觉
# self.变量名 这个变量名可以随便取,self.变量名 可以把它当做是在实例对象内封装了一个初始容器
# 定义个行为(方法)吃饭
def eat(self):
print("%s在吃饭" % self.name) # 不同对象所对应的属性,所以是self.属性名
# 创建一个有初始化数据的对象
# 格式: 对象名 = 类名(多个值)
list1 = [1, 2, 3]
student1 = Student("pgnoe", 160, 180, 3000, list1)
# student2 = Student()错误写法,参数个数不一致
student2 = Student("李小璐", 160, 160, 20000, list1)
# 取值
print(student1.height)
print(student2.height)
student1.eat() # 结果:pgone在吃饭
# 修改值
student1.height = 170
# 不可变类型的数据
print(student1.height) # student1修改了值,发生改变
print(student2.height)
# 可变类型的数据
list1 = [1, 2, 3]
student1 = Student("pgnoe", 160, 180, 3000, list1)
student2 = Student("李小璐", 170, 160, 20000, list1)
student1.likes[0] = 100
print(student1.likes)
print(student2.likes) # 结果:[100,2,3] [100,2,3] 都改变了
深入理解构造方法
class Person:
def __init__(self, name, age, gender): # 构造方法,如果 类名() ,则该方法会被自动执行
self.n1 = name # self就相当于一个容器,self.名字随便取,开辟空间,然后存放 参数/内容
self.n2 = age
self.n3 = gender
def kc(self): # 已经有三个空间了,而且存了内容
data = "%s,性别%s,今年%s岁,喜欢上山砍柴" % (self.n1, self.n3, self.n2)
print(data)
def db(self):
data = "%s,性别%s,今年%s岁,喜欢开车去东北" % (self.n1, self.n3, self.n2)
print(data)
def bj(self):
data = "%s,性别%s,今年%s岁,喜欢大宝剑" % (self.n1, self.n3, self.n2)
print(data)
obj = Person('老狗', 20, '男') # 创建一个对象(实例化),构造方法就会被执行
# obj就是传给前面的self的
obj.kc()
obj.db()
obj.bj()
class Person():
def __init__(self, name):
self.name = name
self.age = 19 # 已经使用默认值,上面的参数列表可以不用写参数
one = Person("min") # 默认带2个值
# print(one.name,one.age) 结果:min 19 证明了,one=self
# 作用1:将数据封装到对象中,以供自己在方法中调用
class FileHandler: # 读取一个文件
def __init__(self, file_path): # 通过构造方法来封装
self.file_path = file_path
self.f = open(self.file_path, 'rb') # self. 就是一个封装过程
# 将每次都要打开文件的操作,放在初始化的位置
def read_first(self):
self.f.read()
# ...
pass
def read_last(self):
# self.f.read()
# ...
pass
def read_second(self):
# self.f...
# ...
pass
obj = FileHandler('C:/xx/xx.log')
obj.read_first()
obj.read_last()
obj.read_second()
obj.f.close()
# 作用2:将数据封装到对象中,以供其他函数调用
def new_fun(arg):
print(arg.k1)
arg.k2
arg.k3
class Foo:
def __init__(self, k1, k2, k3):
self.k1 = k1
self.k2 = k2
self.k3 = k3
rig = Foo(11, 22, 33) # 将数据封装在rig对象中
new_fun(rig)
>>>应用案列:
class User_info:
def __init__(self):
self.name = None # self提供一个容器,它的真正名字是self.后面的
def info(self):
print('当前用户名称:%s' % (self.name,))
def account(self):
print('当前用户%s的账单是:....' % (self.name,))
def shopping(self):
print('%s购买了一个人形抱枕' % (self.name,))
def login(self):
user = input('请输入用户名:')
pwd = input('请输入密码:')
if pwd == 'yep':
self.name = user # 让容器 中存放 user的值,self.name 表示读取值
while True:
print("""
1. 查看用户信息
2. 查看用户账单
3. 购买抱枕
""")
num = int(input('请输入选择的序号:'))
if num == 1:
self.info() # 可以写作 obj.info()
elif num == 2:
self.account()
elif num == 3:
self.shopping()
else:
print('序号不存在,请重新输入')
else:
print("登录失败")
obj = User_info()
obj.login()
class Student(object):
def __init__(self, name, height, weigth, money):
self.name = name
self.height = height
self.weigth = weigth
self.money = money
print("这就是构造函数")
# 定义个行为(方法)吃饭
def eat(self):
print("%s在吃饭" % self.name)
def __del__(self):
print('这是析构函数')
obj = Student('肖战',60,185,100000000)
obj.eat()
del obj
析构函数
__del__”就是一个析构函数了,当使用del 删除对象时,会调用他本身的析构函数,
另外当对象在某个作用域中调用完毕,在跳出其作用域的同时析构函数也会被调用一次,这样可以用来释放内存空间。
del()也是可选的,如果不提供,则Python 会在后台提供默认析构函数
如果要显式的调用析构函数,可以使用del关键字: del obj
# 第一种:
# 当程序运行的完毕的时候,对象会自动的销毁
student1 = Student("pingone",160,180,138000)
# 第二种:手动的释放内存,即删除变量
del student1
while True:
pass
# 第二种,在函数内
# 当一个变量是在函数中定义的,当这个函数执行完毕的时候,这个变量会自动的释放---建议代码写在函数中
def func():
student1 = Student("胖胖",160,180,178000)
print("调用了函数")
func()
while True:
pass
# 和变量手动删除的区别:
num1 = 1
num2 = 1
num3 = 1
del num1 # 要全部删除三个,内存才会得到释放
class Student(object):
def __init__(self, name, height, weigth, money):
self.name = name
self.height = height
self.weigth = weigth
self.money = money
def eat(self):
print("%s在吃饭" % self.name)
# 当我们执行print方法打印该对象时,会 自动的调用__str__ 方法,方便程序员来查看值的
# 如果一个对象的属性需要大量打印,可以重写__str__方法
# 如果不写__str__函数,默认是打印对象的类型和内存地址
def __str__(self):
return "name:%s,height:%d,weigth:%d,money:%d" % (self.name, self.height, self.weigth, self.money)
# 查看所有的数据
student1 = Student("林俊杰", 175, 160, 100000)
# print(student1.name,student2,money...)
# print("name:%s,height:%d,weigth:%d,money:%d"%(student1.name,student1.height,student1.weigth,student1.money))
print(student1)
student2 = Student("周杰伦", 170, 160, 100)
# print("name:%s,height:%d,weigth:%d,money:%d"%(student2.name,student2.height,student2.weigth,student2.money))
print(student2)
类的成员可以分为三大类:字段(变量)、方法和属性
所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。
而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份。
class Foo:
country = "中国" # 类变量(静态字段)
def __init__(self, name):
self.name = name # 实例变量(字段)
def func(self):
pass
# 准则:
# 实例变量(字段)访问时,使用对象访问,即: obj1.name
# 类变量(静态字段)访问时,使用类访问,即: Foo.country (实在不方便时,才使用对象去访问)
obj1 = Foo('周杰伦')
obj2 = Foo('林俊杰')
print(obj1.name) # 周杰伦
print(Foo.country) # 中国
"""
obj1 = Foo('周杰伦')
obj2 = Foo('林俊杰')
# obj1.name = 'alex'
# print(obj1.name) # alex
# print(obj2.name) # 林俊杰
# obj1.country = '美国'
# print(obj1.country) # 美国
# print(obj2.country) # 中国
# Foo.country = '美国'
# print(obj1.country) # 美国
# print(obj2.country) # 美国
"""
什么时候用类变量?
当所有对象中有共同的字段时且要改都改要删都删时,
可以将 实例变量(字段) 提取到 类变量(静态字段)
# 公有 类变量
class Foo:
country = "中国"
def __init__(self):
pass
def func(self):
# 内部调用
# print(self.country) 内部 对象来调用
print(Foo.country) # 内部 类来调用 (推荐)
# 外部调用
obj = Foo()
print(Foo.country) # 外部 类 来调用 (推荐)
# print(obj.country) 外部对象来调用
obj.func()
# 私有 类变量
class Foo:
__country = "中国"
def __init__(self):
pass
def func(self):
# 内部调用
print(self.__country)
print(Foo.__country) # 推荐
@classmethod # 类方法
def func(cls):
print(cls.__country)
@staticmethod # 静态方法
def func():
print(Foo.__country) # 这两种来访问类的私有变量,外部都可以直接用类名.函数名来访问
@property
def func(self):
return Foo.__country
# 外部无法调用私有类变量
obj = Foo()
# print(Foo.country)报错,无法直接调用 私有 类变量
obj.func() # 用对象来调用 实例方法
Foo.func(obj) # 自己传对象obj
Foo.func()
print(obj.func) # 当做属性来访问
# 公有实例变量(字段)
class Foo:
def __init__(self, name):
self.name = name
self.age = 123
def func(self):
print(self.name) # 内部调用
obj = Foo('周杰伦')
print(obj.name) # 外部调用
print(obj.age) # 外部调用
obj.func()
# 私有实例变量
class Foo:
def __init__(self, name):
# 私有实例变量(私有字段)
self.__name = name
self.age = 123
def func(self):
print(self.__name) # 内部可调用
obj = Foo('林俊杰')
print(obj.age)
# obj.__name # 无法访问
obj.func() # 找一个内部人:func, 让func帮助你执行内部私有 __name
# 深入理解 私用实例变量
class Student(object):
def __init__(self, name, age, height, weigth, money):
self.name = name
# 在python语言中,在一个属性前加上 __(两个下划线),表示该属性是私有的属性,私有的属性在类的外部不能直接访问
# 如果外部想操作私有变量, 可以提供公开的get/set方法来获取/设置该属性
# 使数据安全
self.__age = age
self.height = height
self.weigth = weigth
self.money = money
# 获取__age属性值
def getAge(self):
return self.__age
# 设置__age值
def setAge(self, age):
# 处理数据
if age > 16 and age < 24: # 这样可以 设置变量条件
self.__age = age
else:
print("您不符合入学年龄要求") # get 和 set 其实就内部访问变量
def eat(self):
print("%s在吃饭" % self.name)
# 创建一个对象
student1 = Student("张家辉", 18, 180, 120, 100000)
# 修改年龄
print(student1.age)
student1.age = -19 # 报错,已经无法访问私有变量
print(student1.__age) # 注意: 在类的外部不能直接访问私有属性
# 通过get方法访问私有变量
print(student1.getAge())
# 通过set方法来设置私有变量
student1.setAge(19)
student1.setAge(-19) # 还可以在在setAge函数里设置条件
print(student1.getAge())
# 在python中,一个私有变量如 __A 在python解释器解释的时候,其实就是将 __A的名字改成了
# _类名__A 的形式,而我们可以通过 _类名__A的形式去访问/设置该变量(不建议这么做)
# 注意:
print(student1._Student__age)
student1._Student__age = -19
print(student1.getAge())
# 没必要写实例方法
class Foo(object):
def __init__(self,name):
self.name = name
def func(self):
print('123') # 这里没有用到初始化,或者对象功能
obj = Foo('雷军')
obj.func()
# 有必要写实例方法
"""
class Foo(object):
def __init__(self, name):
self.name = name
def func(self):
print(self.name)
obj = Foo('雷军')
obj.func()
"""
对于没有必要写实例化方法,可采取静态方法
class Foo(object):
def __init__(self, name):
self.name = name
# 实例方法
def func(self):
print(self.name)
# 静态方法,如果方法无需使用对象中封装的值,那么就可以使用静态方法
@staticmethod
def display(): # 参数self可以省略
print(666)
def display(a1, a2): # 也可以传参
return a1 + a2 # 返回值
obj = Foo('周杰伦')
obj.func()
# obj.dispaly() 可以,但不推荐
Foo.dispaly() # 静态方法,可以直接用类调用
ret = Foo.display(1, 3)
print(ret)
# 静态方法 总结
1. 编写时:
方法上方写 @staticmethod
方法参数可有可无
2. 调用时:
类.方法名() (推荐)
对象.方法名()
3. 什么时写静态方法?
无需调用对象中已封装的值
class Foo(object):
def __init__(self, name):
self.name = name
# 实例方法,self是对象
def func(self):
print(self.name)
# 静态方法,如果方法无需使用对象中封装的值,那么就可以使用静态方法
@staticmethod
def display(a1, a2):
return a1 + a2
# 类方法,cls是类
@classmethod
def show(cls, x1, x2):
print(cls, x1, x2)
# inner = cls("min") cls能获取到当前类
# inner.func() 结果:min
# 执行类方法
Foo.show(1, 8) # 结果: 1 8
# 类方法 总结
1. 定义时:
方法上方写: @classmethod
方法的参数: 至少有一个cls参数
2. 执行时:
类名.方法名() #默认会将当前类传到参数中.
3. 什么时用?
如果在方法中会使用到当前类,那么就可以使用类方法.
# 私有实例化方法
class Foo(object):
def __init__(self):
pass
def __display(self,arg):
print('私有方法',arg)
def func(self):
self.__display(123) # 内部定义一个访问实例方法
obj = Foo()
# obj.__display(123) # 无法访问
obj.func() # 通过内部方法来访问
# 私有的静态方法
class Foo(object):
def __init__(self):
pass
@staticmethod
def __display(arg):
print('私有静态 方法', arg)
def func(self): # 实例化方法 在外部通过实例化对象来调用
Foo.__display(123) # 本来可以用self.__display(123),但建议用类
@staticmethod
def get_display(): # 通过在内部再创建一个静态方法以供调用
Foo.__display(888)
# Foo.__display(123) 报错
obj = Foo()
obj.func()
Foo.get_display()
# 两个结果一样,都可以实现
Python中的属性其实是普通方法的变种。@property 将方法变成属性
属性有两种定义方式
多用于:会改变的量,要用于计算得到
# 属性的定义和使用
# ############### 定义 ###############
class Foo:
def func(self):
pass
# 定义属性
@property
def prop(self):
pass
# ############### 调用 ###############
foo_obj = Foo()
foo_obj.func()
foo_obj.prop #调用属性
# 由属性的定义和调用要注意一下几点:
# 定义时,在普通方法的基础上添加 @property 装饰器;
# 定义时,属性仅有一个self参数
# 调用时,无需括号
# 调用方法:foo_obj.func()
# 调用属性:foo_obj.prop
# 注意:属性存在意义是 访问属性时可以制造出和访问字段完全相同的假象
# 属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能。
>>>完成一个分页练习
class Page(object):
def __init__(self, li, pagesize):
self.li = li
self.pagesize = pagesize
def start(self): # 第一页
return self.li[:self.pagesize]
def end(self): # 最后一页
return self.li[(self.total_num - 1) * self.pagesize:self.total_num * self.pagesize]
def index(self): # 跳转页
page_num = int(input("please enter page num:"))
if page_num < 1 and page_num > self.total_num:
print("you enter page num is wrong!")
return self.li[(page_num - 1) * self.pagesize:page_num * self.pagesize]
@property
def total_num(self): # 计算总页数
if len(self.li) % self.pagesize == 0:
return len(self.li) // self.pagesize
elif len(self.li) % self.pagesize != 0:
return len(self.li) // self.pagesize + 1
obj = Page([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3)
print(obj.start())
print(obj.end())
print(obj.index())
属性的定义有两种方式:
装饰器 即:在方法上应用装饰器
静态字段 即:在类中定义值为property对象的静态字段
装饰器方式:在类的普通方法上应用@property装饰器
# Python中的类有经典类和新式类,新式类的属性比经典类的属性丰富。
#( 如果类继object,那么该类是新式类 )
# 经典类,具有一种@property装饰器(如上一步实例)
# ############### 定义 ###############
class Goods:
@property
def price(self):
return "wupeiqi"
# ############### 调用 ###############
obj = Goods()
result = obj.price # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
# 新式类,具有三种@property装饰器
# ############### 定义 ###############
class Goods(object):
@property
def price(self):
print '@property'
@price.setter
def price(self, value):
print '@price.setter'
@price.deleter
def price(self):
print '@price.deleter'
# ############### 调用 ###############
obj = Goods()
obj.price # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
obj.price = 123 # 自动执行 @price.setter 修饰的 price 方法,并将 123 赋值给方法的参数
del obj.price # 自动执行 @price.deleter 修饰的 price 方法
'''
经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法
新式类中的属性有三种访问方式,并分别对应了
三个被@property、@方法名.setter、@方法名.deleter修饰的方法
'''
由于新式类中具有三种访问方式,我们可以根据他们几个属性的访问特点,
分别将三个方法定义为对同一个属性:获取、修改、删除
class Goods(object):
def __init__(self):
# 原价
self.original_price = 100
# 折扣
self.discount = 0.8
@property
def price(self):
# 实际价格 = 原价 * 折扣
new_price = self.original_price * self.discount
return new_price
@price.setter
def price(self, value):
self.original_price = value
@price.deltter
def price(self, value):
del self.original_price
obj = Goods()
obj.price # 获取商品价格
obj.price = 200 # 修改商品原价
del obj.price # 删除商品原价
静态字段方式,创建值为property对象的静态字段
# 当使用静态字段的方式创建属性时,经典类和新式类无区别
class Foo:
def get_bar(self):
return 'wupeiqi'
BAR = property(get_bar)
obj = Foo()
reuslt = obj.BAR # 自动调用get_bar方法,并获取方法的返回值
print reuslt
property的构造方法中有个四个参数
第一个参数是方法名,调用 对象.属性 时自动触发执行方法
第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法
第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法
第四个参数是字符串,调用 对象.属性.__doc__ ,此参数是该属性的描述信息
class Foo:
def get_bar(self):
return 'wupeiqi'
# *必须两个参数
def set_bar(self, value):
return return 'set value' + value
def del_bar(self):
return 'wupeiqi'
BAR = property(get_bar, set_bar, del_bar, 'description...')
obj = Foo()
obj.BAR # 自动调用第一个参数中定义的方法:get_bar
obj.BAR = "alex" # 自动调用第二个参数中定义的方法:set_bar方法,并将“alex”当作参数传入
del Foo.BAR # 自动调用第三个参数中定义的方法:del_bar方法
obj.BAE.__doc__ # 自动获取第四个参数中设置的值:description...
由于静态字段方式创建属性具有三种访问方式,我们可以根据他们几个属性的访问特点,
分别将三个方法定义为对同一个属性:获取、修改、删除
class Goods(object):
def __init__(self):
# 原价
self.original_price = 100
# 折扣
self.discount = 0.8
def get_price(self):
# 实际价格 = 原价 * 折扣
new_price = self.original_price * self.discount
return new_price
def set_price(self, value):
self.original_price = value
def del_price(self, value):
del self.original_price
PRICE = property(get_price, set_price, del_price, '价格属性描述...')
obj = Goods()
obj.PRICE # 获取商品价格
obj.PRICE = 200 # 修改商品原价
del obj.PRICE # 删除商品原价
注意:Python WEB框架 Django 的视图中 request.POST
就是使用的静态字段的方式创建的属性
# Django源码
class WSGIRequest(http.HttpRequest):
def __init__(self, environ):
script_name = get_script_name(environ)
path_info = get_path_info(environ)
if not path_info:
# Sometimes PATH_INFO exists, but is empty (e.g. accessing
# the SCRIPT_NAME URL without a trailing slash). We really need to
# operate as if they'd requested '/'. Not amazingly nice to force
# the path like this, but should be harmless.
path_info = '/'
self.environ = environ
self.path_info = path_info
self.path = '%s/%s' % (script_name.rstrip('/'), path_info.lstrip('/'))
self.META = environ
self.META['PATH_INFO'] = path_info
self.META['SCRIPT_NAME'] = script_name
self.method = environ['REQUEST_METHOD'].upper()
_, content_params = cgi.parse_header(environ.get('CONTENT_TYPE', ''))
if 'charset' in content_params:
try:
codecs.lookup(content_params['charset'])
except LookupError:
pass
else:
self.encoding = content_params['charset']
self._post_parse_error = False
try:
content_length = int(environ.get('CONTENT_LENGTH'))
except (ValueError, TypeError):
content_length = 0
self._stream = LimitedStream(self.environ['wsgi.input'], content_length)
self._read_started = False
self.resolver_match = None
def _get_scheme(self):
return self.environ.get('wsgi.url_scheme')
def _get_request(self):
warnings.warn('`request.REQUEST` is deprecated, use `request.GET` or '
'`request.POST` instead.', RemovedInDjango19Warning, 2)
if not hasattr(self, '_request'):
self._request = datastructures.MergeDict(self.POST, self.GET)
return self._request
@cached_property
def GET(self):
# The WSGI spec says 'QUERY_STRING' may be absent.
raw_query_string = get_bytes_from_wsgi(self.environ, 'QUERY_STRING', '')
return http.QueryDict(raw_query_string, encoding=self._encoding)
# ############### 看这里看这里 ###############
def _get_post(self):
if not hasattr(self, '_post'):
self._load_post_and_files()
return self._post
# ############### 看这里看这里 ###############
def _set_post(self, post):
self._post = post
@cached_property
def COOKIES(self):
raw_cookie = get_str_from_wsgi(self.environ, 'HTTP_COOKIE', '')
return http.parse_cookie(raw_cookie)
def _get_files(self):
if not hasattr(self, '_files'):
self._load_post_and_files()
return self._files
# ############### 看这里看这里 ###############
POST = property(_get_post, _set_post)
FILES = property(_get_files)
REQUEST = property(_get_request)
所以,定义属性共有两种方式,分别是【装饰器】和【静态字段】,
而【装饰器】方式针对经典类和新式类又有所不同。
slots 是特殊的属性,是类自己维护的, 用来限制可以使用的属性
class Student(object):
# __slots__ 是特殊的属性,是类自己维护的, 用来限制可以使用的属性
# __slots__ 的值是元组的形式, 每个元素是当前类可以使用的属性名,除此以外的不能使用
# 单词“slots”:“插槽”
__slots__ = ("age", "name", "hello", "yyy", "money", "eat")
# 参数里为啥要加上eat,因为它虽然是方法,却也被当成了属性
def __init__(self, name, age):
self.name = name
self.age = age
student1 = Student("周杰伦", 35)
# python是动态语言,可以动态的给对象添加属性, 使程序更加的灵活
# 动态添加的属性,只针对当前对象有效, 其他对象不能使用
# 动态添加的属性
student1.money = 10000 # 限制随意的添加属性,设置“__slots__”中有的属性,才能添加
print(student1.name)
print(student1.money)
student2 = Student("林俊杰", 32)
# student2.money=4000
print(student2.money) # 没有自己对象上一步主动添加字段,代码会报错;不能使用studen1添加的字段
# 想在类的外部添加一个方法
# 动态的添加行为(方法)
# 错误示范:
def eat(self): # 已经是在类的外部了
print("%s在吃饭"%(self.name))
# student2.eat() 错误,类的内部没有定义eat()
# 第一种方式:
def eat(self): #已经是在类的外部了,这参数self可以写成任意
print("%s在吃饭"%(self.name))
print("%s在吃饭"%(self.eat)) #在吃饭
# 这已经不能算方法了,就是一个外部函数
student2.eat = eat # 这句必须写在外置函数后,因为函数名相当于声明了变量
# 相当于在 构造函数里 增添了一句:self.eat=eat
# 测试:在吃饭,所以是把函数名eat封装到对象中了
# 调用
student2.eat(student2) # 调用外部eat函数,将对象传入,对象中已经封装好了name属性
# 打印结果:林俊杰在吃饭
# 第二种方式:
from types import MethodType
def eat(self):
print("%s在吃饭" % (self.name))
# 动态的添加行为
student2.eat = MethodType(eat, student2) # 将eat函数添加到对象种
student2.eat()
class Student(object):
def __init__(self, name, age):
self.name = name
self.__age = age
# 第一种获取私有字段的方式:
# def setAge(self,age):
# self.__age = age
# def getAge(self):
# return self.__age
# 方法名是将私有变量名 去掉2个下划线 后的名字命名的
# 并且在方法名上 加上 @property 表示私有变量,可以用:对象名.去掉下划线的名字 去访问变量
@property # 原单词的意思:所有物,写了这个表示把age当做属性来用了
def age(self):
return self.__age
# 方法名是将私有变量名 去掉2个下划线 后的名字命名的
# 并且在方法名上 @去掉2个下滑线后的名字.setter
# 表示私有变量 可以以:对象名.去掉下划线的名字 = 值 的方式去修改值
@age.setter
def age(self, age):
self.__age = age
student1 = Student("胖胖", 35)
# 私有属性是不能直接以 对象名.属性名 的方式调用的
# print(student1.__age) 错误
# 希望以 对象名.属性名 的方式去访问私有属性
print(student1.age) # 35
# 如果没有加@property 的话,要这样访问:print(student1.age())#
student1.age = 100 # 修改内容
print(student1.age)
class Base(object):
def f1(self):
print('5个功能')
class Foo(object):
# class Foo(Base):
def f1(self):
print('3个功能')
Base.f1(self) # 主动其他类的方法,因为其他类的方法需要一个参数,所以要手动的添加self
# 这个self 是 还是当前的对象
obj = Foo()
obj.f1()
# Base.实例方法(自己传self),类名.实例方法;与继承没有关系
# 类的方法可以主动去执行其他类里非私有的方法,但是参数需要自己传
按照类的继承顺序(执行继承体系的下一个),按顺序找下一个类,去执行它的内容,
找不到就会报错
# super
'''
class Base(object):
def f1(self):
print('5个功能')
class Foo(Base):
def f1(self):
super().f1()
print('3个功能')
obj = Foo()
obj.f1()
'''
class Foo(object):
def f1(self):
super().f1()
print('3个功能')
class Bar(object):
def f1(self):
print('6个功能')
class Info(Foo, Bar):
pass
# obj = Foo()
# obj.f1() 结果:报错,因为Foo没有继承的类,找不到
obj = Info()
obj.f1() # 6个功能 3个功能 过程:去执行下一个继承类的f1()
super().实例方法
super() 函数是用于调用父类(超类)的一个方法。
super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,
会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。
MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。
#单继承 中的 super()
class A:
def __init__(self):
self.n = 2
def add(self, m):
print('self is {0} @A.add'.format(self))
self.n += m #此时的self是B的对象,所以 self.n=3+2
class B(A):
def __init__(self):
self.n = 3 #执行后,B的对象改成了5
def add(self, m):
print('self is {0} @B.add'.format(self))
super().add(m)
self.n += 3 #b.n=b.n+3
b = B()
b.add(2)
print(b.n)
'''
输出结果:
self is <__main__.B object at 0x000002198A7B2320> @B.add
self is <__main__.B object at 0x000002198A7B2320> @A.add
8
1、super().add(m) 确实调用了父类 A 的 add 方法。
2、super().add(m) 调用父类方法 def add(self, m) 时, 此时父类中 self 并不是父类的实例而是子类的实例, 所以 b.add(2) 之后的结果是 5 而不是 4 。
'''
#多继承中的super()
class A:
def __init__(self):
self.n = 2
def add(self, m):
print('self is {0} @A.add'.format(self))
self.n += m
class B(A):
def __init__(self):
self.n = 3
def add(self, m):
print('self is {0} @B.add'.format(self))
super().add(m)
self.n += 3
class C(A):
def __init__(self):
self.n = 4
def add(self, m):
print('self is {0} @C.add'.format(self))
super().add(m)
self.n += 4
class D(B, C):
def __init__(self):
self.n = 5
def add(self, m):
print('self is {0} @D.add'.format(self))
super().add(m)
self.n += 5
d = D()
d.add(2)
print(d.n)
'''
输出结果:
self is <__main__.D object at 0x10ce10e48> @D.add
self is <__main__.D object at 0x10ce10e48> @B.add
self is <__main__.D object at 0x10ce10e48> @C.add
self is <__main__.D object at 0x10ce10e48> @A.add
19
'''
class A():
def __init__(self):
print('enter A') # 4
print('leave A') # 5
class B(A):
def __init__(self):
print('enter B') # 2
super().__init__()
print('leave B') # 7
class C(A):
def __init__(self):
print('enter C') # 3
super().__init__()
print('leave C') # 6
class D(B, C):
def __init__(self):
print('enter D') # 1
super().__init__()
print('leave D') # 8
d = D()
"""
---> B ---
A --| |--> D
---> C ---
结果:
enter D
enter B
enter C
enter A
leave A
leave C
leave B
leave D
"""
# 作用:输出描述类的信息
class Foo:
""" 这是我写的信息,看我 """
def func(self):
pass
print(Foo.__doc__)
# 结果:这是我写的信息,看我
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
lib/aa.py 文件中
# !/usr/bin/env python
# -*- coding:utf-8 -*-
class C:
def __init__(self):
self.name = 'jaychou'
---
from lib.aa import C
obj = C()
print obj.__module__ # 输出 lib.aa,即:输出模块
print obj.__class__ # 输出 lib.aa.C,即:输出类
类和对象默认都是可哈希(hash)的;但是可以给对象设置成不可hash,类不能更改
class Foo(object):
对象.__hash__= None # 此时对象就变不可哈希了
# 类和对象默认都是可哈希(hash)的,即是不可变的,也就可以作为字典的键
class Foo():
def eat(self, food):
print("我爱吃鱼和:", food)
class Bar():
def eat(self, food):
print("我爱吃肉和:", food)
dic = {Foo: "鸡蛋", Bar: "包子"} # 字典的键
for k, v in dic.items():
k().eat(v)
# 类名:就是一个变量名
'''
输出结果:
我爱吃鱼和: 鸡蛋
我爱吃肉和: 包子
'''
__hash__触发时机
# __hash__ 的触发的场景有两种:
#(1)被内置函数hash()调用
#(2)hash类型的集合对自身成员的hash操作:
# -- set(), frozenset([iterable]), dict(**kwargs)
class A:
def __init__(self):
self.a = 1
self.b = 2
def __hash__(self):
return hash(str(self.a)+str(self.b))
a = A()
print(hash(a))
__ eq__的触发时机
# == 时触发
class A:
def __init__(self):
self.a = 1
self.b = 2
def __eq__(self,obj):
if self.a == obj.a and self.b == obj.b:
return True
a = A()
b = A()
print(a == b)
------
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# def __hash__(self):
# print(self.name, '使用了__hash__方法')
# return hash(self.name)
def __eq__(self, other):
print(self.name, '使用了__eq__方法')
return self.__dict__ == other.__dict__
# __dict__取出对象封装的所有值,再使用bool判断,返回True
# 和上例子中↓ 是一样的:
'''
if self.a == obj.a and self.b == obj.b:
return True
'''
person1 = Person('周杰伦', 20)
person2 = Person('林俊杰', 20)
person3 = Person('陈奕迅', 30)
person4 = Person('周杰伦', 20)
print(person1 == person4)
print(person2 == person3)
'''
输出结果:
周杰伦 使用了__eq__方法
True
林俊杰 使用了__eq__方法
False
注:以上代码只用到了__eq__,因为没涉及触发__hash__三种;放开注释代码也不会执行
'''
如果代码重写了__eq__没有重写__hash__又使用了
set(), frozenset([iterable]), dict(**kwargs) 哈希集合的元素,那代码会报错
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# def __hash__(self):
# print(self.name, '使用了__hash__方法')
# return hash(self.name)
def __eq__(self, other):
print(self.name, '使用了__eq__方法')
return self.__dict__ == other.__dict__
person1 = Person('周杰伦', 20)
person2 = Person('林俊杰', 20)
person3 = Person('陈奕迅', 30)
person4 = Person('周杰伦', 20)
# print(person1 == person4)
# print(person2 == person3)
set1 = {person1, person2, person3, person4} # 使用了
print(set1)
# 结果报错:TypeError: unhashable type: 'Person'
# 这其实是因为重写__eq__()方法后会默认把__hash__赋为None;如下测试代码:
class A:
def __eq__(self, other):
pass
a = A()
print(a.__hash__) # 设置为了None,变为不可哈希
hash(a) # 此时打印报错:TypeError: unhashable type: 'A'
如果自定义类重写了__hash__()方法没有重写__eq__()方法
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __hash__(self):
print(self.name, '使用了__hash__方法')
return hash(self.name)
# def __eq__(self, other):
# print(self.name, '使用了__eq__方法')
# return self.__dict__ == other.__dict__
person1 = Person('周杰伦', 20)
person2 = Person('林俊杰', 20)
person3 = Person('陈奕迅', 30)
person4 = Person('周杰伦', 20)
set1 = {person1, person2, person3, person4} # 使用了
print(set1)
'''
输出结果:
周杰伦 使用了__hash__方法
林俊杰 使用了__hash__方法
陈奕迅 使用了__hash__方法
周杰伦 使用了__hash__方法
{<__main__.Person object at 0x0000022DB1C29A20>,
<__main__.Person object at 0x0000022DB1C29A58>,
<__main__.Person object at 0x0000022DB1C29A90>,
<__main__.Person object at 0x0000022DB1C29AC8>}
注意:person1与person4是同一个人,内存地址不一样,却被添加到了不同的集合
原因:主要是因为当发现hash出的值相同时,就需要__eq__进行下一步判断。
重写了__hash__却没重写__eq__,于是默认去调用了object的__eq__,
而默认的__eq__比较的是对象地址,person1与persperson4地址不同,
所以将最后的person4加到了新集合。
# 如果上述 __hash__ 和__eq__都重写了
# 输出结果只有三个,相同的共有一个内存地址
{<__main__.Person object at 0x0000026215659A90>,
<__main__.Person object at 0x0000026215659A58>,
<__main__.Person object at 0x0000026215659A20>}
'''
class Person:
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
def __hash__(self):
return hash(self.name+self.sex)
def __eq__(self, other):
if self.name == other.name and self.sex == other.sex:
return True
p_lst = []
for i in range(84):
p_lst.append(Person('jaychou',i,'male'))
print(p_lst) # 打印所有对象内存地址
print(set(p_lst)) #{<__main__.Person object at 0x000001261E1699E8>}
# 去重后打印的结果和第一个元素内存地址一样
class Foo(object):
def __init__(self):
pass
obj = Foo() # obj是通过Foo类实例化的对象
# 上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,
# --Foo类本身也是一个对象,因为在Python中一切事物都是对象。
# 如果按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,
# --那么Foo类对象应该也是通过执行某个类的 构造方法 创建。
print type(obj) # 输出: 表示,obj 对象由Foo类创建
print type(Foo) # 输出: 表示,Foo类对象由 type 类创建
所以,obj对象是Foo类的一个实例,Foo类对象是 type 类的一个实例,
即:Foo类对象 是通过type类的构造方法创建,
那么,创建类就可以有两种方式:
# 普通方式
class Foo(object):
def func(self):
print('hello world')
# 特殊方式(type类的构造函数)
def func(self):
print('hello world')
Foo = type('Foo',(object,), {'func': func})
# type第一个参数:类名
# type第二个参数:当前类的基类
# type第三个参数:类的成员
# 类 是由 type 类实例化产生
# 那么问题来了,类默认是由 type 类实例化产生,type类中如何实现的创建类?
# --类又是如何创建对象?
# 答:类中有一个属性 __metaclass__,其用来表示该类由 谁 来实例化创建,
# --所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程。
class MyType(type):
def __init__(self, what, bases=None, dict=None):
super(MyType, self).__init__(what, bases, dict)
def __call__(self, *args, **kwargs):
obj = self.__new__(self, *args, **kwargs)
self.__init__(obj)
class Foo(object):
__metaclass__ = MyType
def __init__(self, name):
self.name = name
def __new__(cls, *args, **kwargs):
return object.__new__(cls, *args, **kwargs)
# 第一阶段:解释器从上到下执行代码创建Foo类
# 第二阶段:通过Foo类创建obj对象
obj = Foo()
class Foo(object):
def __init__(self, a1, a2):
self.a1 = a1
self.a2 = a2
def __call__(self, *args, **kwargs):
print(11111, args, kwargs)
return 123
def __getitem__(self, item):
print(item)
return 8
def __setitem__(self, key, value):
print(key, value, 111111111)
def __delitem__(self, key):
print(key)
def __add__(self, other):
return self.a1 + other.a2
def __enter__(self):
print('1111')
return 999
def __exit__(self, exc_type, exc_val, exc_tb):
print('22222')
# 类名() 自动执行 __init__
obj = Foo(1,2)
# 对象() 或者 类()() 自动执行 __call__
ret = obj(6,4,2,k1=456)
print(callable(ret)) # True
# 对象['xx'] 自动执行 __getitem__
ret = obj['yu']
print(ret)
# 对象['xx'] = 11 自动执行 __setitem__
obj['k1'] = 123
# del 对象[xx] 自动执行 __delitem__
del obj['uuu']
>>>例子:打印出 123
class Foo(object):
def __init__(self):
self.dic = {}
def __setitem__(self, key, value):
self.dic[key] = value
def __getitem__(self, item):
return self.dic.get(item)
obj = Foo() # 执行__init__ 拿到一个空字典
obj["x"] = 123 # 执行__setitem__ dic[x]=123
print(obj["x"]) # 执行__getitem__ 返回 dic(x)的值,即是123
# 对象+对象 自动执行 __add__
obj1 = Foo(1,2)
obj2 = Foo(88,99)
ret = obj2 + obj1
print(ret)
# with 对象 自动执行 __enter__ / __exit__
obj = Foo(1,2)
with obj as f:
print(f)
print('内部代码')
>>>判断下一个类中的成员个数
class Foo(object):
def __init__(self):
pass
var1 = 1
var2 = 2
__var3 = 3
def func1(self):
pass
def func2(self):
pass
def __func3(self):
pass
def __len__(self):
# lst = []
lst = [k for k in Foo.__dict__ if not (k.startswith('__') and k.endswith('__'))]
return len(lst)
obj = Foo()
ret = len(obj)
print(ret)
dic = {'k': 'v'}
# 对象 : 存储属性 和调用方法
dic['k'] = 'v'
class Foo:
def __init__(self, name):
self.name = name
def __getitem__(self, item):
print(self.__dict__[item])
def __setitem__(self, key, value):
self.__dict__[key] = value
def __delitem__(self, key):
print('del obj[key]时,我执行')
self.__dict__.pop(key)
def __delattr__(self, item):
print('del obj.key时,我执行')
self.__dict__.pop(item)
f1 = Foo('jaychou')
f1['age'] = 18
f1['age1'] = 19
del f1.age1
del f1['age']
f1['name'] = 'eason'
print(f1.__dict__)
'''
del obj.key时,我执行
del obj[key]时,我执行
{'name': 'eason'}
'''
纸牌游戏练习:用到 __len__和__item__系列
from collections import namedtuple # 引入命名元组
# rank 牌面的大小 suit牌面的花色(这个相当于一个只有属性的类)
Card = namedtuple('Card', ['rank', 'suit'])
class FranchDeck:
ranks = [str(n) for n in range(2, 11)] + list('JQKA')
suits = ['红心', '方板', '梅花', '黑桃']
def __init__(self):
self._cards = [Card(rank, suit) for rank in FranchDeck.ranks
for suit in FranchDeck.suits]
def __len__(self):
return len(self._cards)
def __getitem__(self, item):
return self._cards[item]
deck = FranchDeck()
print(deck[0])
from random import choice
print(choice(deck))
print(choice(deck))
'''
Card(rank='2', suit='红心')
Card(rank='K', suit='红心')
Card(rank='J', suit='黑桃')
'''
# 纸牌游戏 二:
from collections import namedtuple
Card = namedtuple('Card', ['rank', 'suit'])
class FranchDeck:
ranks = [str(n) for n in range(2, 11)] + list('JQKA')
suits = ['红心', '方板', '梅花', '黑桃']
def __init__(self):
self._cards = [Card(rank, suit) for rank in FranchDeck.ranks
for suit in FranchDeck.suits]
def __len__(self):
return len(self._cards)
def __getitem__(self, item):
return self._cards[item]
def __setitem__(self, key, value):
self._cards[key] = value
deck = FranchDeck()
print(deck[0])
from random import choice
print(choice(deck))
print(choice(deck))
from random import shuffle
shuffle(deck)
print(deck[:5])
'''
Card(rank='2', suit='红心')
Card(rank='6', suit='黑桃')
Card(rank='K', suit='黑桃')
[Card(rank='7', suit='红心'),
Card(rank='K', suit='黑桃'),
Card(rank='10', suit='梅花'),
Card(rank='2', suit='梅花'),
Card(rank='A', suit='方板')]
'''
对象.属性=值 执行__setattr__
res=对象.属性 执行__getattr__
print(res)
上图中,构造方法self.name不写,在后面print中obj.name调用__getattr__同样可以输出名字
如果不写,会默认继承object类中的init方法,所以同样可以完成
class Foo(object):
def __init__(self):
self.name = "min" # 默认自己类中执行__setattr__
def __setattr__(self, key, value): # 上面进来执行,结果发现,↓↓↓
object.__setattr__(self, key, value) # 主动执行父类中的方法
# 类.函数()需要自己传参数,给对象中设置 name="min"
obj = Foo()
print(obj.name)
obj.xx = 123
这句话:object.__seatter__(self,"info",{})
实质上就是 在__inint__中写一句:self.info={}
因为你执行__setattr__就等于:对象.属性=value
上面 对象中已经设置到了一个字典,后面再给字典中增添一组值
item 也是传入的 key,也就是 name;所以可以通过字典取到值
这就是 初始化设置值得原理
class Foo(object):
def __init__(self):
object.__setattr__(self, "info", {})
def __setattr__(self, key, value):
self.info[key] = value
def __getattr__(self, item):
return self.info[item]
obj = Foo()
obj.xx = 123
obj.name = "min"
print(obj.xx)
print(obj.name) # 结果:123 min
class Foo(object):
def __init__(self, a1, a2): # 它只是初始化方法
"""
为空对象进行数据初始化
:param a1:
:param a2:
"""
self.a1 = a1
self.a2 = a2
def __new__(cls, *args, **kwargs): # 构造方法,创建时,它可以开辟内存
"""
创建一个空对象
:param args:
:param kwargs:
:return:
"""
return object.__new__(cls) # Python内部创建一个当前类的对象(初创时内部是空的)
obj1 = Foo(1, 2)
print(obj1)
obj2 = Foo(11, 12)
print(obj2)
class Foo(object):
def __init__(self):
pass
def func(self):
pass
def __str__(self):
return "min"
obj = Foo()
print(obj, type(obj)) # min
# 打印对象,会自动执行__str__方法,必须要有返回值,而且只能是字符串类型
对象.__dict__使用
# 取出对象封装的所有值
class Foo(object):
def __init__(self, name, age):
self.name = name
self.age = age
def func(self):
pass
obj1 = Foo('格雷西', 99)
obj2 = Foo('路易', 89)
print(obj1.__dict__) # {'name': '格雷西', 'age': 99}
print(obj2.__dict__) # {'name': '路易', 'age': 89}
# l1是list类的一个对象,可迭代对象
l1 = [11,22,33,44]
#l2是list类的一个对象,可迭代对象
l2 = [1,22,3,44]
class Foo(object):
def __init__(self,name,age):
self.name = name
self.age = age
def func(self):
pass
#obj1是Foo类的一个对象,不可迭代对象
obj1 = Foo('周杰伦',99)
for item in obj1:
print(item)
------
class Foo(object):
def __init__(self,name,age):
self.name = name
self.age = age
def func(self):
pass
def __iter__(self): # 只要含有__iter__就是可迭代对象了
return iter([11,22,33,44,55,66])
yield 11
yield 22
yield 33
# obj1是Foo类的一个对象,可迭代对象
"""
如果想要把不可迭代对象 -> 可迭代对象
1. 在类中定义__iter__方法
2. iter内部返回一个迭代器(生成器也是一种特殊迭代器)
"""
obj1 = Foo('雷格西',99)
for item in obj1:
print(item)
class StarkConfig(object):
def __init__(self, num):
self.num = num
def run(self):
self()
def __call__(self, *args, **kwargs):
print(self.num)
class RoleConfig(StarkConfig):
def __call__(self, *args, **kwargs):
print(345)
def __getitem__(self, item):
return self.num[item]
v1 = RoleConfig("abex")
v2 = StarkConfig("wupeiqi")
print(v1[1]) # ret=self.num[1] alex[1] b
print(v2[2]) # 报错,没有 __getitem__
# 关于调用 类变量 和实例变量 的问题
# 第一题:
class StarkConfig(object):
list_display = []
def get_list_display(self):
self.list_display.insert(0, 33)
return self.list_display
class RoleConfig(StarkConfig):
list_display = [11, 22]
s1 = StarkConfig()
result1 = s1.get_list_display()
print(result1) # 33
result2 = s1.get_list_display()
print(result2) # 33 33
# 第二题:
class StarkConfig(object):
list_display = []
def get_list_display(self):
self.list_display.insert(0, 33)
return self.list_display
class RoleConfig(StarkConfig):
list_display = [11, 22]
s1 = StarkConfig()
s2 = StarkConfig()
result1 = s1.get_list_display()
print(result1) # [33]
result2 = s2.get_list_display()
print(result2) # [33, 33]
# 第三题:
class StarkConfig(object):
list_display = []
def get_list_display(self):
self.list_display.insert(0, 33)
return self.list_display
class RoleConfig(StarkConfig):
list_display = [11, 22]
s1 = StarkConfig()
s2 = RoleConfig()
result1 = s1.get_list_display()
print(result1) # [33]
result2 = s2.get_list_display() # 继承后,先找自己,再找父类
print(result2) # [33, 11, 22]
# 第四题:
class StarkConfig(object):
list_display = []
def get_list_display(self):
self.list_display.insert(0, 33)
return self.list_display
class RoleConfig(StarkConfig):
list_display = [11, 22]
s1 = RoleConfig()
s2 = RoleConfig()
result1 = s1.get_list_display()
print(result1) # [33, 11, 22]
result2 = s2.get_list_display()
print(result2) # [33, 33, 11, 22]
# issubclass
# 两个参数都必须是类
class Base(object):
pass
class Foo(Base):
pass
class Bar(Foo):
pass
print(issubclass(Bar, Base)) # 检查第一个类是否是第二个参数的 子子孙孙类
告知当前的数据类型;获取当前对象是由那个类创建,精准的告知对象的数据类型
class Foo(object):
pass
obj = Foo()
print(obj, type(obj)) # 获取当前对象是由那个类创建
if type(obj) == Foo:
print('obj是Foo类型')
>>>练习
class Foo(object):
pass
class Bar(object):
pass
def func(*args):
foo_counter = 0
bar_counter = 0
for item in args:
if type(item) == Foo:
foo_counter += 1
elif type(item) == Bar:
bar_counter += 1
return foo_counter, bar_counter
# result = func(Foo(),Bar(),Foo())
# print(result)
v1, v2 = func(Foo(), Bar(), Foo())
print(v1, v2) # (2,1)
只能向上判断,不能向下判断
class Base(object):
pass
class Foo(Base):
pass
obj1 = Foo()
print(isinstance(obj1, Foo)) # 检查第一个参数(对象)是否是第二个参数(类及父类)的实例。
print(isinstance(obj1, Base)) # 检查第一个参数(对象)是否是第二个参数(类及父类)的实例。
obj2 = Base()
print(isinstance(obj2, Foo)) # False
print(isinstance(obj2, Base)) # True
def jud(a, b):
if isinstance(a, int or float) and isinstance(a, int or float):
return a + b
'''
总结:
给你一个参数,判断对象是不是由某一个指定类? type --> type(obj) == Foo
给你一个参数,判断对象是不是由某一个指定类或其父类? isinstance --> instance(obj,Foo)
'''
>>>模拟一个射击游戏
class Base(object):
def __init__(self, name, gender, life_value, weapon):
self.name = name
self.gender = gender
self.life_value = life_value
self.weapon = weapon
class Police(Base):
def introduction(self):
tpl = "我是%s,我是警察" % (self.name)
print(tpl)
def attack(self, other):
if isinstance(other, Police):
print("警察是自己人,不能开枪")
return
print("警察%s,开枪射击匪徒%s" % (self.name, other.name))
self.life_value -= 10
other.life_value -= 100
class Badman(Base):
def introduction(self):
tpl = "我是%s,我是匪徒" % (self.name)
print(tpl)
def attack(self, other):
if isinstance(other, Badman):
print("匪徒是自己人,不能开枪")
return
print("匪徒%s,开枪射击警察%s" % (self.name, other.name))
self.life_value -= 10
other.life_value -= 100
j1 = Police("周杰伦", "男", 100, "枪")
j2 = Police("梁朝伟", "男", 100, "枪")
b1 = Badman("吴彦祖", "男", 1000, "机关枪")
b2 = Badman("刘德华", "男", 1000, "机关枪")
j1.attack(j2) # 警察是自己人,不能开枪
j1.attack(b1) # 警察周杰伦,开枪射击匪徒吴彦祖
b1.attack(b2) # 匪徒是自己人,不能开枪
from types import MethodType, FunctionType
def check(arg):
"""
检查arg是方法还是函数?
:param arg:
:return:
"""
if isinstance(arg, MethodType): # 注意是 实例对象
print('arg是一个方法')
elif isinstance(arg, FunctionType):
print('arg是一个函数')
else:
print('不知道是什么')
def func():
pass
class Foo(object):
def detail(self):
pass
@staticmethod
def xxx():
pass
@classmethod
def ppp(cls):
pass
check(func) # 直接将对象 func传入,万物皆对象
obj = Foo()
check(obj.detail) # 方法
check(Foo.detail) # 函数
check(obj.xxx)
check(Foo.xxx) # 对于静态方法都是 函数
check(obj.ppp)
check(Foo.ppp) # 对于类方法都是 方法
'''
输出结果:
arg是一个函数
arg是一个方法
arg是一个函数
arg是一个函数
arg是一个函数
arg是一个方法
arg是一个方法
'''
# 特点:
class Foo(object):
def f1(self):
pass
def f2(self):
pass
def f3(self):
pass
# obj = Foo()
# print(obj.f1)
# print(obj.f2) #这样实例调用时,里面的都是方法
obj = Foo()
Foo.f1(obj) # 直接用类名调用函数,会把f1当做函数,不会自动传入对象,所以要自己手动传入(不推荐使用)
# 这是一种名称空间的调用方法,实质上是函数
obj = Foo()
obj.f1() # 把f1当做方法,自动传self值
# 练习
class Foo(object):
def f1(self):
pass
def f2(self):
pass
def f3(self):
pass
list_display = [f1, f2]
obj = Foo()
Foo.list_display.append(obj.f3) # 对象.函数名 调用
for item in Foo.list_display:
print(item)
总结:到底方法函数?
对象.xxx --> xxx就是方法
类.xxx --> xxx就是函数
xxx --> xxx就是函数
执行某个动作是,需要其他类来帮助完成(用到其他对象),此时关系是最紧密的
# 模拟一个植物大战僵尸的场景
class Zhiwu(object):
def __init__(self, name, hp, at):
self.name = name
self.hp = hp
self.at = at
def attack(self, js):
print("植物开始攻击僵尸")
js.hp -= self.at
print(f"僵尸掉了{self.at}血,僵尸还剩下{js.hp}血")
class Janshi(object):
def __init__(self, name, hp, at):
self.name = name
self.hp = hp
self.at = at
def attack(self, zw):
print("僵尸开始攻击植物")
zw.hp -= self.at
print(f"植物掉了{self.at}血,植物还剩下{zw.hp}血")
zw = Zhiwu("豌豆射手", 10, 100)
js = Janshi("铁桶", 1000, 1)
zw.attack(js)
js.attack(zw)
zw.attack(js)
js.attack(zw)
我需要你,而且你还要是属于我
# 一对一的关系
class Boy(object):
def __init__(self, name, girl_friend=None):
self.girl_friend = girl_friend # 一对一体现在这
# 在初始化时可以给一个对象的属性设置成另一个类的对象
def eat(self):
if self.girl_friend:
print(f"带着女朋友{self.girl_friend.name}一起吃饭")
else:
print('自己一个人吃饭')
def movie(self):
if self.girl_friend:
print(f"带着女朋友{self.girl_friend.name}一起看电影")
else:
print('自己一个人看电影')
class Girl(object):
def __init__(self, name):
self.name = name
b = Boy("敏浩")
g = Girl("美丽")
b.eat()
b.girl_friend = g
b.eat()
# 一对多的关系
class School(object):
def __init__(self, name):
self.teacher_li = []
def zhaoping(self, teacher):
self.teacher_li.append(teacher)
def shangke(self):
for i in self.teacher_li:
print(f'{i.name}在上课')
class Teacher(object):
def __init__(self, name):
self.name = name
school = School("白马")
t1 = Teacher("min")
t2 = Teacher("like")
t3 = Teacher("yep")
school.zhaoping(t1)
school.zhaoping(t2)
school.zhaoping(t3)
school.shangke() # 一个学校多个老师在上课
"""
创建三个学校且三个学校的设施内容等都是一致.
"""
class School(object):
def __init__(self, name, address):
self.name = name
self.address = address
def speech(self):
print('讲课')
obj1 = School('北京校区', '朝阳')
obj2 = School('上海校区', '浦东新区')
obj3 = School('深圳校区', '南山区')
class Teacher(object):
def __init__(self, name, age, salary):
self.name = name
self.age = age
self.__salary = salary
self.school = None # 等于给他准备一个空盒子,就是不用参数
t1 = Teacher('周杰伦', 19, 188888)
t2 = Teacher('林俊杰', 18, 60)
t3 = Teacher('陈奕迅', 16, 900000)
# 老师分配校区
t1.school = obj1
t2.school = obj1
t3.school = obj2
# 查看t1老师,所在的校区名称/地址
print(t1.school.name)
print(t1.school.address)
print(t1.name)
print(t1.age)
t1.school.speech()
在学完了python的class机制之后,遇到一个生产中的问题,还是会懵逼,这其实太正常了,因为任何程序的开发都是先设计后编程,
python的class机制只不过是一种编程方式,如果你硬要拿着class去和你的问题死磕,变得更加懵逼都是分分钟的事,在以前,
软件的开发相对简单,从任务的分析到编写程序,再到程序的调试,可以由一个人或一个小组去完成。但是随着软件规模的迅速增大,
软件任意面临的问题十分复杂,需要考虑的因素太多,在一个软件中所产生的错误和隐藏的错误、未知的错误可能达到惊人的程度,
这也不是在设计阶段就完全解决的。
所以软件的开发其实一整套规范,我们所学的只是其中的一小部分,一个完整的开发过程,需要明确每个阶段的任务,
在保证一个阶段正确的前提下再进行下一个阶段的工作,称之为软件工程
面向对象的软件工程包括下面几个部:
1.面向对象分析(object oriented analysis ,OOA)
软件工程中的系统分析阶段,要求分析员和用户结合在一起,对用户的需求做出精确的分析和明确的表述,
从大的方面解析软件系统应该做什么,而不是怎么去做。面向对象的分析要按照面向对象的概念和方法,在对任务的分析中,
从客观存在的事物和事物之间的关系,贵南出有关的对象(对象的‘特征’和‘技能’)以及对象之间的联系,
并将具有相同属性和行为的对象用一个类class来标识。
建立一个能反映这是工作情况的需求模型,此时的模型是粗略的。
2 面向对象设计(object oriented design,OOD)
根据面向对象分析阶段形成的需求模型,对每一部分分别进行具体的设计。
首先是类的设计,类的设计可能包含多个层次(利用继承与派生机制)。
然后以这些类为基础提出程序设计的思路和方法,包括对算法的设计。
在设计阶段并不牵涉任何一门具体的计算机语言,而是用一种更通用的描述工具(如伪代码或流程图)来描述
3 面向对象编程(object oriented programming,OOP)
根据面向对象设计的结果,选择一种计算机语言把它写成程序,可以是python
4 面向对象测试(object oriented test,OOT)
在写好程序后交给用户使用前,必须对程序进行严格的测试,测试的目的是发现程序中的错误并修正它。
面向对的测试是用面向对象的方法进行测试,以类作为测试的基本单元。
5 面向对象维护(object oriendted soft maintenance,OOSM)
正如对任何产品都需要进行售后服务和维护一样,软件在使用时也会出现一些问题,
或者软件商想改进软件的性能,这就需要修改程序。
由于使用了面向对象的方法开发程序,使用程序的维护比较容易。
因为对象的封装性,修改一个对象对其他的对象影响很小,利用面向对象的方法维护程序,
大大提高了软件维护的效率,可扩展性高。
在面向对象方法中,最早发展的肯定是面向对象编程(OOP),那时OOA和OOD都还没有发展起来,
因此程序设计者为了写出面向对象的程序,还必须深入到分析和设计领域,尤其是设计领域,
那时的OOP实际上包含了现在的OOD和OOP两个阶段,这对程序设计者要求比较高,许多人感到很难掌握。
现在设计一个大的软件,是严格按照面向对象软件工程的5个阶段进行的,这个5个阶段的工作不是由一个人从头到尾完成的,
而是由不同的人分别完成,这样OOP阶段的任务就比较简单了。程序编写者只需要根据OOd提出的思路,用面向对象语言编写出程序既可。
在一个大型软件开发过程中,OOP只是很小的一个部分。
对于全栈开发的你来说,这五个阶段都有了,对于简单的问题,不必严格按照这个5个阶段进行,
往往由程序设计者按照面向对象的方法进行程序设计,包括类的设计和程序的设计
1.面向对象的程序设计看起来高大上,所以我在编程时就应该保证通篇class,这样写出的程序一定是好的程序
(面向对象只适合那些可扩展性要求比较高的场景)
2.在基于面向对象编程时,不一定要定义的类中完整的包含这三种特性
3.类有类属性,实例有实例属性,所以我们在定义class时一定要定义出那么几个类属性,想不到怎么办,
那就使劲的想,定义的越多越牛逼;这就犯了一个严重的错误,程序越早面向对象,死的越早,为啥面向对象,
因为我们要将数据与功能结合到一起,程序整体的结构都没有出来,或者说需要考虑的问题你都没有搞清楚个八九不离十,
你就开始面向对象了,这就导致了,你在那里干想,自以为想通了,定义了一堆属性,结果后来又都用不到,
或者想不通到底应该定义啥,那就一直想吧,想着想着就疯了。
# 抽象/实现
抽象指对现实世界问题和实体的本质表现,行为和特征建模,建立一个相关的子集,可以用于 绘程序结构,从而实现这种模型。
抽象不仅包括这种模型的数据属性,还定义了这些数据的接口。
对某种抽象的实现就是对此数据及与之相关接口的现实化(realization)。现实化这个过程对于客户 程序应当是透明而且无关的。
# 封装/接口
封装描述了对数据/信息进行隐藏的观念,它对数据属性提供接口和访问函数。通过任何客户端直接对数据的访问,
无视接口,与封装性都是背道而驰的,除非程序员允许这些操作。作为实现的 一部分,客户端根本就不需要知道在封装之后,
数据属性是如何组织的。在Python中,所有的类属性都是公开的,但名字可能被“混淆”了,以阻止未经授权的访问,
但仅此而已,再没有其他预防措施了。这就需要在设计时,对数据提供相应的接口,
以免客户程序通过不规范的操作来存取封装的数据属性。
注意:封装绝不是等于“把不想让别人看到、以后可能修改的东西用private隐藏起来”
真正的封装是,经过深入的思考,做出良好的抽象,给出“完整且最小”的接口,并使得内部细节可以对外透明
(注意:对外透明的意思是,外部调用者可以顺利的得到自己想要的任何功能,完全意识不到内部细节的存在)
# 合成
合成扩充了对类的 述,使得多个不同的类合成为一个大的类,来解决现实问题。
合成 述了 一个异常复杂的系统,比如一个类由其它类组成,更小的组件也可能是其它的类,数据属性及行为,
所有这些合在一起,彼此是“有一个”的关系。
# 派生/继承/继承结构
派生描述了子类衍生出新的特性,新类保留已存类类型中所有需要的数据和行为,
但允许修改或者其它的自定义操作,都不会修改原类的定义。
继承描述了子类属性从祖先类继承这样一种方式
继承结构表示多“代”派生,可以述成一个“族谱”,连续的子类,与祖先类都有关系。
# 泛化/特化
基于继承
泛化表示所有子类与其父类及祖先类有一样的特点。
特化描述所有子类的自定义,也就是,什么属性让它与其祖先类不同。
# 多态与多态性
多态指的是同一种事物的多种状态:水这种事物有多种不同的状态:冰,水蒸气
多态性的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需考虑他们具体的类。
冰,水蒸气,都继承于水,它们都有一个同名的方法就是变成云,但是冰.变云(),
与水蒸气.变云()是截然不同的过程,虽然调用的方法都一样
# 自省/反射
自省也称作反射,这个性质展示了某对象是如何在运行期取得自身信息的。如果传一个对象给你,你可以查出它有什么能力,
这是一项强大的特性。如果Python不支持某种形式的自省功能,dir和type内建函数,将很难正常工作。
还有那些特殊属性,像__dict__,__name__及__doc__
'''
实现:下面功能
1. while循环提示⽤户输⼊:⽤户名、密码、邮箱(正则满⾜邮箱格式)
2. 为每个⽤户创建⼀个对象,并添加到列表中。
3. 当列表中的添加了3个对象后,跳出循环并以此循环打印所有⽤户的姓名和邮箱。
'''
import re
class User:
def __init__(self, name, pwd, email):
self.name = name
self.pwd = pwd
self.email = email
class Account:
def __init__(self): # 定义一个列表
self.user_list = []
def register(self):
while True:
name = input('请输入用户名:')
pwd = input('请设置密码:')
email = input("请输入邮箱:")
str_email = "[0-9a-zA-Z][\w\-.]+@[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)*\.[A-Za-z0-9]{2,6}"
if (re.search(str_email, email)).group() == email:
usr = User(name, pwd, email) # 将输入的符合要求的内容,封装在usr对象中
self.user_list.append(usr) # 将循环等到的不同对象添加到列表中
if len(self.user_list) == 3:
break
def show_info(self):
for user in self.user_list: # 遍历列表
print("姓名是:%s,密码是:%s,邮箱是:%s" % (user.name, user.pwd, user.email))
def run(self): # 启动程序
self.register()
self.show_info()
if __name__ == '__main__': # 入口
obj = Account()
obj.run()
>>>实现用户注册和登录功能,将用户名和密码以一个对象的形式存在一个列表中
class User:
def __init__(self, name, pwd):
self.name = name
self.pwd = pwd
class Account:
def __init__(self):
self.user_list = []
def register(self): # 注册
for i in range(2):
name = input('请输入用户名:')
pwd = input('请设置密码:')
usr = User(name, pwd)
self.user_list.append(usr)
def login(self): # 登录,校验
name = input('请输入账号:')
pwd = input('请输入密码:')
flag = False
for user in self.user_list:
if name == user.name and pwd == user.pwd:
flag = True
break
if flag: # 此时的flag已经变成 True 了
print('登录成功')
else:
print('登录失败')
def run(self): # 运行
self.register()
self.login()
if __name__ == '__main__':
obj = Account()
obj.run()
>>>题目:模拟 人开枪,射击子弹
#创建一个包:person_shooting
person_shooting.py # 主程序
'''
人 开 枪 射击 子弹
类名:人Person
属性: 枪
行为: 开枪
类名: 枪
属性: 弹夹
行为: 射击
类名: 弹夹
属性: 子弹的数量
行为: 装弹, 获取弹夹剩余的子弹数量
'''
格式: from 模块名 import 函数/属性/类
from person1 import Person
from gun2 import Gun
from clip3 import Clip
# 创建弹夹
clip=Clip()
clip.set_number(30)
# 创建枪
gun= Gun(clip) # 枪要弹夹
# 创建人
person=Person(gun) # 人需要枪
# 为什么这两个对象需要参数?因为开始为它们设置了构造函数默认的参数
# 开枪
for i in range(1,31):
person.shoot()
person1.py
#人 开 枪 射击 子弹
#类名:人Person
#属性: 枪
#行为: 开枪, 换弹夹
class Person(object):
def __init__(self,gun):# 创建时,人就带有枪,所以要用构造函数
self.gun=gun
def shoot(self):# 开枪,定义一个shoot方法
self.gun.shooting()# 让具体对象的枪,调用一个shooting方法,射击
gun2.py
#类名: 枪
#属性: 弹夹
#行为: 射击
class Gun(object):
def __init__(self,clip): # 创建自带弹夹
self.clip=clip
def shooting(self): # 枪的射击方法
# 从弹夹取出子弹
# 弹夹可能是空的
# 弹夹可能只有1颗子弹
number=self.clip.get_number() # 使用私有变量获取到子弹的数量,然后赋值给number是方便
#print(number)
if number<=0:
print("您已经没有子弹了,请更换弹夹")
elif number==1:
self.clip.set_number(number-1)
print("子弹打完了,请更换弹夹")
elif number<=30:
self.clip.set_number(number-1)
print("您打了1发子弹,剩余%d" % (self.clip.get_number()))
clip3.py
# 类名: 弹夹
# 属性: 子弹的数量
# 行为: 装弹, 获取弹夹剩余的子弹数量
class Clip(object):
def __init__(self):
self.__number=0 # 将子弹设为私有变量
def get_number(self):
return self.__number
def set_number(self,num):
if num>=1 and num <= 30:
self.__number=num
else:
print("你已经没有子弹了,请装弹")
# 在哪个设置了“私有变量”,该类里,一定要有 get 和 set 函数
# get一般不需要参数,return self.私有变量 就行
# set 可以设置参数,一般是来设置私有变量后将值赋给私有变量
# 类的成员练习
class Foo(object):
a1 = 11
a2 = 22
def __init__(self):
self.a1 = 1
obj = Foo()
print(obj.a1)
print(obj.a2) # 输出结果:1,22
# 2
class Foo(object):
a1 = 11
def __init__(self, num):
self.a2 = num
obj = Foo(999)
print(obj.a2) # 999
print(obj.a1) # 11
print(Foo.a1) # 11
print(Foo.a2) # 报错,因为变量已经被封装到对象里了,不能直接访问
# 3
class Foo(object):
a1 = 1
__a2 = 2
def __init__(self, num):
self.num = num
self.__salary = 1000
def get_data(self):
print(self.num + self.al)
obj = Foo(666)
print(obj.num) # 666
print(obj.a1) # 1
print(obj.__salary) # 报错
print(obj.__a2) # 报错
print(Foo.a1) # 1
print(Foo.__a2) # 报错,类都不能直接访问私有变量
# 4
class Foo(object):
a1 = 1
__a2 = 2
def __init__(self, num):
self.num = num
self.__salary = 100
0
# self.a1=99
def get_data(self):
print(self.num + self.al)
obj1 = Foo(666)
obj2 = Foo(999)
print(obj1.num) # 666
print(obj1.a1) # 1
obj1.num = 18
obj1.a1 = 99
print(obj1.num) # 18
print(obj1.a1) # 99 先去对象中查找,没找到,会重新创建一个 self.a1=99
print(obj2.a1) # 1
print(obj2.num + Foo.a1) # 1000
print(obj2.num + obj1.a1) # 1098
# 嵌套练习
class School:
def __init__(self, address):
self.address = address
bj = School('北京校区')
sh = School('上海校区')
sz = School('深圳校区')
class Course(object):
def __init__(self, name, period, price, school=None):
self.name = name
self.period = period
self.price = price
self.school = school
# self.school=None
py1 = Course('Python全栈', 110, 19999, bj)
py2 = Course('Python全栈', 110, 19999, sh)
py3 = Course('Python全栈', 110, 19999, sz)
l1 = Course('Linux运维', 110, 19999, bj)
l2 = Course('Linux运维', 110, 19999, sh)
g1 = Course('Go开发', 119, 19999, bj)
class Grade(object):
def __init__(self, name, people, introduce, course=None):
self.name = name
self.people = people
self.introduce = introduce
self.course = course
gr1 = Grade('全栈1期', 20, '....', py1)
gr2 = Grade('全栈1期', 20, '....', py2)
gr3 = Grade('Linux8期', 20, '....', l2)
gr1.people
gr1.course.price
gr1.course.school.address
# 类和字典 的组合
class StarkConfig(object):
def __init__(self, num):
self.num = num
def changelist(self, request):
print(self.num, request)
def run(self):
self.changelist(999)
class RoleConfig(StarkConfig):
def changelist(self, request):
print(666, self.num)
class AdminSite(object):
def __init__(self):
self._registry = {}
def register(self, k, v):
self._registry[k] = v
site = AdminSite() # 拿到一个空字典
site.register('lyd', StarkConfig(19))
site.register('yjl', StarkConfig(20))
site.register('fgz', RoleConfig(33)) # 新增三个键值对 {lyd:StarkConfig(19),....}
# print(len(site._registry)) # 3
for k, row in site._registry.items():
row.run() # 输出结果:19,999 20,99 666,333
# StarkConfig(19).run()
# 变换
class UserInfo(object):
pass
name = "min"
class Department(object):
pass
name = "good"
class StarkConfig(object):
def __init__(self, num):
self.num = num
def changelist(self, request):
print(self.num, request)
# Useinfo,999
# print(self.num.name,request) 可以通过对象调用自己内部成员
def run(self):
self.changelist(999)
class RoleConfig(StarkConfig):
def changelist(self, request):
print(666, self.num)
class AdminSite(object):
def __init__(self):
self._registry = {}
def register(self, k, v):
self._registry[k] = v(k)
site = AdminSite() # 空字典
site.register(UserInfo, StarkConfig)
site.register(Department, StarkConfig) # {UserInfo,StarkConfig(UserInfo),....}
# print(len(site._registry))
for k, row in site._registry.items():
row.run()
# StarkConfig(UserInfo).run()
# self.num=Userinfo
# 升级题
class UserInfo(object):
pass
class Department(object):
pass
class StarkConfig(object):
def __init__(self, num):
self.num = num # self.num=UserInfo
def get_vals(self):
v = [11, 22, 33]
extra = self.extra_vals()
if extra: # None 为假
v.extend(extra)
return v
def extra_vals(self):
pass
def run(self):
return self.get_vals()
class RoleConfig(StarkConfig):
def extra_vals(self):
return [99, 88]
class AdminSite(object):
def __init__(self):
self._registry = {}
def register(self, k, v):
self._registry[k] = v(k)
site = AdminSite() # 空字典
site.register(UserInfo, StarkConfig)
site.register(Department, StarkConfig) # {UserInfo,StarkConfig(UserInfo),....}
for k, row in site._registry.items():
print(row.run()) # ret=StarkConfig(UserInfo).run()
# ret=self.get_vals()
# 结果:[11,22,33] [11,22,33]
# 总结:
# 类加了括号(一起出现)就是对象,对象里的参数优先赋值到构造函数里实例化,
# --类调用函数,当前self就是该类