创建和使用类
在面向对象编程中,类是对具有相同属性和行为的一个或多个对象的抽象描述。
实例是由某个特定的类所描述的一个具体对象。根据类来创建对象的过程被称为实例化。
面向对象编程的三个特点:封装(信息隐藏)、继承和多态。
注意: 定义类时类名首字母应该大写
init()方法(初始化方法)
-
方法init(),类中的函数称为方法。方法init()是一个特殊的方法,每当你根据Dog类创建新实例时,Python都会自动运行它。在这个方法的名称中,开头和末尾各有两个下划线,这是一种约定,旨在避免Python默认方法与普通方法发生名称冲突。
-
类方法调用时,self形参会自动传递。只需要给self后面的形参提供实参值;
-
方法init()两个变量都有前缀self。以self为前缀的变量都可供类中的所有方法使用,我们还可以通过类的任何实例来访问这些变量。self.name = name获取存储在形参name中的值,并将其存储到变量name中,然后该变量被关联到当前创建的实例。 self.age = age的作用与此类似。像这样可通过实例访问的变量称为属性。
使用类和实例
通常可以认为首字母大写的名称(如Dog )指的是类,而小写的名称(如my_dog )指的是根据类创建的实例(示例如下)。通过实例对象 对方法进行调用 或者对属性进行访问可以对属性值进行访问和修改(只有非私有的方法和属性才可以)。
class Dog:
def __init__(self,name,age):
"""初始化属性name和age"""
self.name=name
self.age=age
def sit(self):
"""模拟小狗被命令时蹲下"""
print(self.name.title()+"坐下了")
def shout(self):
"""模拟小狗大叫"""
print(self.name+"叫了!")
dog1=Dog("mary",18)#创建类的实例
dog2=Dog("joy",20)#创建类的实例
dog1.sit()#输出结果:Mary坐下了
dog2.shout()#输出结果:joy叫了!
私有属性和私有方法
私有属性和私有方法(private)的前面需要加上”__”两条下划线,这样该属性和方法就不能被外部所使用,且不能被子类继承(私有属性和私有方法容易引起代码混乱,一般不建议使用)。
"单下划线" 开始的成员变量叫做保护变量(protect),意思是只有类对象和子类对象自己能访问到这些变量;“双下划线” 开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据。
class Persson:
def __init__(self, name):
self.name = name
self.__age = 18
def __secret(self):
print("%s 的年龄是:%s" % (self.name, self.__age))
persson= Persson("小明")
print(persson.__age)
persson.__secret()
运行肯定是会报错的
那么我们如何像java一样访问到对象的私有属性和方法呢,java通过反射,python却没有那么麻烦,翻看python底层实现我们发现,python底层对私有方法的处理是在私有方法前加入了_对象名,由此我们可以通过如下方式访问到私有属性和方法
class Persson:
def __init__(self, name):
self.name = name
self.__age = 18
def __secret(self):
print("%s 的年龄是:%s" % (self.name, self.__age))
persson= Persson("小明")
print(persson._Persson__age)
persson._Persson__secret()
因为下划线对解释器有特殊的意义,而且是内建标识符所使用的符号,我们建议程序员避免用下划线作为变量名的开始。一般来讲,变量名_object被看作是“私有 的”,在模块或类外不可以使用,不能用'from moduleimport *'导入。当变量是私有的时候,用_object来表示变量是很好的习惯。因为变量名__object__对Python 来说有特殊含义,对于普通的变量应当避免这种命名风格。
python有关private的描述,python中不存在**protected的概念**,要么是public要么就是private,但是python中的private不像C++, Java那样,它并不是真正意义上的private,通过name mangling(名称改编(目的就是以防子类意外重写基类的方法或者属性),即前面加上“单下划线”+类名,eg:_Class__object)机制就可以访问private了。
"单下划线" 开始的成员变量叫做**保护变量**,意思是只有类对象和子类对象自己能访问到这些变量;"双下划线" 开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据。
以单下划线开头(_foo)的代表不能直接访问的类属性,需通过类提供的接口进行访问,不能用“from xxx import *”而导入;以双下划线开头的(__foo)代表类的私有成员;以双下划线开头和结尾的(__foo__)代表python里特殊方法专用的标识,如 __init__
class Fruit:
def __init__(self, name, price, shop):
self.name = name # 常规属性对象可以直接打点使用
self._shop = shop # 单下划线属性,打点调用不提示,且可以强制使用
self.__price = price # 双下划线属性,打点调用不提示,且不能强制使用
def resetPrice(self, newPrice):
self.__price = newPrice
#常规方法
def info(self):
print("在" + self._shop + "此时的" + self.name + "需要" + self.__price + "元/斤")
apple = Fruit("苹果", "5.00", "超市")
apple.info()
print(apple.name)#输出结果:苹果(常规方法对象可以直接打点使用)
print(apple._shop)#输出结果:超市(单下划线方法,打点调用不提示,且可以强制使用)
#print(apple.__price)#会报错,原因:'Fruit' object has no attribute '__price'(双下划线属性,打点调用不提示,且不能强制使用)
继承
1、创建子类时,必须在括号内指定父类的名称;
2、super()是一个特殊函数(圆括号不能少),用来引用父
类(即超类);
3、在类中需要使用到的属性,尽量在__init__方法中先进
行定义(同样适用于父类);
4、子类中相同名称的方法和属性,将会覆盖父类中的同
名方法和属性;
5、除非万不得已,应尽量避免使用多重继承(使用多重继
承时,只需要在括号中依次列出父类名即可)。
# 父类
class Fruit:
def __init__(self, name, price, shop):
self.name = name # 常规属性对象可以直接打点使用
self._shop = shop # 单下划线属性,打点调用不提示,且可以强制使用
self.__price = price # 双下划线属性,打点调用不提示,且不能强制使用
def resetPrice(self, newPrice):
self.__price = newPrice
def getPrice(self):
return self.__price
# 常规方法
def info(self):
print("在" + self._shop + "此时的" + self.name + "需要" + str(self.__price) + "元/斤")
# 单下划线方法
def _sunOfPay(self, weight):
print("一共消费" + str(weight * self.__price) + "元")
# 双下划线方法()
def __flat(self):
print("我是父类,此方法只有我能用,我的子类都用不了")
# 子类
class Pear(Fruit):
def __init__(self, price, shop, color):
super().__init__("梨", price, shop)
self.color = color
# 重写父类的常规方法(子类能够使用父类的常规方法)
def info(self):
print("在" + self._shop + "此时的" + self.color + "的" + self.name + "需要" + str(self.getPrice()) + "元/斤")
# 重写父类中的保护方法(子类能够使用父类的保护方法)
def _sunOfPay(self, weight):
print("你买的" + str(weight) + "斤梨子花费了" + str(self.getPrice() * weight) + "元")
pear1 = Pear(4.99, "超市", "黄色")
pear1.info() # 输出结果:在超市此时的黄色的梨需要4.99元/斤
pear1._sunOfPay(3) # 输出结果:你买的3斤梨子花费了14.97元
类中的方法
# 返回实例pear1是那个类创建的 a.__class__
print(pear1.__class__) # 输出结果:<class '__main__.Pear'>
# 返回类Pear的基类a.__bases__
print(Pear.__bases__) # 输出结果:(<class '__main__.Fruit'>,)
# issubclass(a, b):判断类a是否为类b的子类;
print(issubclass(Pear, Fruit)) # 输出结果:True
# isinstance(a, b):判断a是否为类b的实例;
print(isinstance(pear1, Fruit)) # 输出结果:True
# hasattr(a, „b‟):判断对象a是否包含方法b。
print(hasattr(Pear, 'info')) # 输出结果:True
在模块中导入类
通过from 模块名(a.py) import 类名(b.class,c.class…) 导入
导入单个:from a import b
导入多个类:from a import b,c,d
导入所有类:from a import *
fruitModule.py文件
# 父类
class Fruit:
def __init__(self, name, price, shop):
self.name = name # 常规属性对象可以直接打点使用
self._shop = shop # 单下划线属性,打点调用不提示,且可以强制使用
self.__price = price # 双下划线属性,打点调用不提示,且不能强制使用
def resetPrice(self, newPrice):
self.__price = newPrice
def getPrice(self):
return self.__price
# 常规方法
def info(self):
print("在" + self._shop + "此时的" + self.name + "需要" + str(self.__price) + "元/斤")
# 单下划线方法
def _sunOfPay(self, weight):
print("一共消费" + str(weight * self.__price) + "元")
# 双下划线方法()
def __flat(self):
print("我是父类,此方法只有我能用,我的子类都用不了")
# 子类1
class Pear(Fruit):
def __init__(self, price, shop, color):
super().__init__("梨子", price, shop)
self.color = color
# 重写父类的常规方法(子类能够使用父类的常规方法)
def info(self):
print("在" + self._shop + "此时的" + self.color + "的" + self.name + "需要" + str(self.getPrice()) + "元/斤")
# 重写父类中的保护方法(子类能够使用父类的保护方法)
def _sunOfPay(self, weight):
print("你买的" + str(weight) + "斤梨子花费了" + str(self.getPrice() * weight) + "元")
# 子类2
class Apple(Fruit):
def __init__(self, price, shop, color):
super().__init__("苹果", price, shop)
self.color = color
# 重写父类的常规方法(子类能够使用父类的常规方法)
def info(self):
print("在" + self._shop + "此时的" + self.color + "的" + self.name + "需要" + str(self.getPrice()) + "元/斤")
# 重写父类中的保护方法(子类能够使用父类的保护方法)
def _sunOfPay(self, weight):
print("你买的" + str(weight) + "斤苹果花费了" + str(self.getPrice() * weight) + "元")
在同一包中的另一模块中导入fruitModule中的类
from fruitModule import Pear,Apple
pear1=Pear(6.00,"水果店","白色")
pear1.info()
pear1._sunOfPay(3.5)
apple1=Apple(5.00,"超市","红色")
apple1.info()
apple1._sunOfPay(2)
property函数
Python中访问对象的属性可以这么做:实例名.变量名。但是有些人却不同意这种访问方法,他们觉得这样做破坏了封装的原则,对象的状态对于外部应该是隐藏的。因此,Python中更推荐使用私有特性,通过在名字前面加上双下划线,然后getter,setter方法访问这些特性。
然而,将“实例名.变量名”的方式改为使用getter、setter方法将使代码变得更加复杂臃肿。如果有一种方法能够使简单的访问特性又能保持代码的规范性就好了。property正是解决这一问题的方案。
Property函数的定义为:
property(fget=None, fset=None, fdel=None, doc=None)
其中,fget是获取属性值的函数,fset是设置属性值的函数,fdel是删除属性的函数,doc是一个文档字符串。property函数的参数都是可选的,若没有参数,产生的属性既不可读也不可写。若只有一个取值方法,产生的属性是只读的。
class Test:
def __init__(self):
self._x = None
def get_x(self):
print("I'm the getter method")
return self._x
def set_x(self, value):
print("I'm the setter method")
self._x = value
def del_x(self):
print("I'm the deleter method")
del self._x
# 设置doc参数
x = property(get_x, set_x, del_x, "I'm the 'x' property.")
# 其实x也是一个类属性,只不过,如果通过了Test的实例t去访问,就会访问到对应的getx setx delx等方法上。
t = Test()
t.x = '100' # 等同于t.set_x("100") 输出结果:I'm the setter method
print(t.x) # 等同于print(t.get_x()) 输出结果:I'm the getter method
事实上,property并不是一个真正的函数,它是拥有许多特殊方法的类:get、set、del。这三个方法合在一起定义了描述符规则。实现了任何一个方法的对象就可称为描述符(descriptor)。当程序读取一个特性时,若该特性被绑定到实现了__get__方法的对象上时,那么就会调用__get__方法
property除了上述这种用法外,还可以使用@property实现相同的功能。
class Test:
def __init__(self):
self._x = None
@property
def x(self):
print("I'm the getter method")
return self._x
@x.setter
def x(self, value):
print("I'm the setter method")
self._x = value
@x.deleter
def x(self):
print("I'm the deleter method")
del self._x
t = Test()
t.x = '100' # 等同于t.set_x("100") 输出结果:I'm the setter method
print(t.x) # 等同于print(t.get_x()) 输出结果:I'm the getter method
类属性(静态属性)
python中并没有像 C语言 C++ java 那样定义静态属性的关键字 static,其实类属性就差不多相当于静态属性.
静态方法和类方法
静态方法(由@staticmethod装饰的方法)、类方法(由
@classmethod装饰的方法),可以被类或类的实例对象调用。
区别:实例方法,第一个参数必须要默认传实例对象,一
般习惯用self。静态方法,参数没有要求。类方法,第一个
参数必须要默认传类,一般习惯用cls。
class Foo(object):
"""类三种方法语法形式"""
def instance_method(self):
print("类{}实例方法,只能被实例对象调用".format(Foo))
@staticmethod
def static_method():
print("我是静态方法")
@classmethod
def class_method(cls):
print("我是类方法")
Foo.static_method()#输出结果:我是静态方法
Foo.class_method()#输出结果:我是类方法
为什么不直接使用静态方法?
静态方法是通过类名来操作类属性的写死在程序中,而类方法是通过参数来操作类属性的,如果子类继承了使用静态方法的类,那么子类继承的静态方法还是在操作父类,子类需要重写静态方法才能操作子类,类方法如果被继承,那么类参数会传入子类本身。
class Diff:
def __init__(self):
print("---")
@property
def dizhi(self):
return self.size
@staticmethod
def setA():
Diff.a = 10
@classmethod
def setB(cls):
cls.b = 20
Diff.setA()
print(Diff.a)#输出结果:10
迭代器
可迭代对象
使用for循环遍历取值的对象叫做可迭代对象, 比如:列表、元组、字典、集合、range、字符串
可迭代的对象说明是可以被迭代(遍历)的,但是只有迭代器可以使用!
什么是迭代器
迭代器是访问可迭代对象的工具
迭代器是指用iter(obj)函数返回的对象(实例)
迭代器是指用next(it)函数获取可迭代对象的数据
list1=[1,2,3,4,5]
it=iter(list1)#让list1提供一个能访问自己的迭代器
print(next(it))#输出结果:1
print(next(it))
print(next(it))
print(next(it))
print(next(it))
迭代器函数(iter和next)
使用 for 循环遍历取值的过程叫做迭代,比如:使用for循环遍历列表获取值的过程。
iter(iterable)从可迭代对象中返回一个迭代器,iterable必须是能提供一个迭代器的对象
、 next(iterator) 从迭代器iterator中获取下一了记录,如果无法获取下一条记录,则触发stoptrerator异常
说明:
1.迭代器只能往前取值,不会后退
2.用iter函数可以返回一个可迭代对象的迭代器
自定义可 迭代 对象
自定义可迭代对象: 在类里面定义__iter__方法创建的对象就是*可迭代(遍历)对象*
class MyList(object):
def __init__(self,age):
# 初始化
self.age = age
def __iter__(self):
# 返回本身
return self
#说明:这个类是可迭代对象,实现了__iter__ 方法。
#到这里只能说明,是可以被迭代(遍历),
# 如同现在就是一个列表;可以做为遍历取值的对象。"
# 现在它只是迭代器的一部分,只实现了两个方法中的一个。
自定义 迭代器
自定义迭代器对象: 在类里面定义iter和next方法创建的对象就是迭代器对象。
这里同时实现两个方法,所以叫迭代器。
class MyClass(object):
"""功能:实现迭代出任意范围数字的值;
例如:遍历出1到100的数字。
"""
# 初始化
def __init__(self, age):
self.age = age
self.num = 0
# 作用:把这个类变成一个可迭代对象
def __iter__(self):
return self
# 作用:在迭代(遍历)时,取出下一个值
def __next__(self):
"""根据条件实现的功能主要是写在这!"""
# 判断是否大于遍历的值
if self.num >= self.age:
# 停止迭代(遍历)
raise StopIteration
else:
# 继续迭代(遍历)直到满足条件为止
self.num += 1
return self.num
#创建对象
MyClass = MyClass(10)
#使用for循环遍历,说明这个类是迭代器。
for x in MyClass:
print(x)在这里插入代码片