- 面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤。
- 面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。
- 优点是:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。
- 缺点是:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身。
- 应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。
面向对象的程序设计的核心是对象(上帝式思维),要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。
当下的程序员应该都听过"面向对象编程"一词,也经常有人问能不能用一句话解释下什么是"面向对象编程",我们先来看看比较正式的说法。
"把一组数据结构和处理它们的方法组成对象(object),把相同行为的对象归纳为类(class),通过类的封装(encapsulation)隐藏内部细节,通过继承(inheritance)实现类的特化(specialization)和泛化(generalization),通过多态(polymorphism)实现基于对象类型的动态分派。"
- 优点:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。
- 缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法预测最终结果。于是我们经常看到一个游戏人某一参数的修改极有可能导致阴霸的技能出现,一刀砍死3个人,这个游戏就失去平衡。
- 应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。
在讲类之前需要了解一些名词:类、对象、实例、实例化
- 类:具有相同特征的一类事物(人、狗、老虎)
- 对象/实例:具体的某一个事物比如小狗类中的旺财(特指某一条狗)
- 实例化:类——>对象的过程
在Python中可以使用class关键字定义类,然后在类中通过之前学习过的函数来定义方法,这样就可以将对象的动态特征描述出来,代码如下所示。
比如创建一个Dog类,为即将创建的所有狗对象绑定name和age两个属性,并且赋予每条创建的狗坐下( set() )的能力
class Dog:#创建一个狗类 role = 'dog' # 狗的角色属性都是狗 # __init__是一个特殊方法用于在创建对象时进行初始化操作 # 通过这个方法我们可以为狗对象绑定name和age两个属性 def __init__(self, name, age): self.name = name self.age = age def sit(self): '''模仿小狗接到命令(对象调用该函数)时坐下''' print('小狗已经坐下了')
写在类中的函数,我们通常称之为(对象的)方法,这些方法就是对象可以接收的消息。
实例化类其他编程语言中一般用关键字 new,但是在 Python 中并没有这个关键字,类的实例化类似函数调用方式。 以下使用类的名称Dog来实例化,并通过 init 方法接收参数。
上面我们创建了一个狗类,但是要实例化一条实实在在的独一无二的狗那该怎么操作呢?
实例化一只实实在在的旺财
wc = Dog('旺财' , 1) #创造了一只具体的狗(旺财)
您可以使用点号 . 来访问对象的属性。使用如下类的名称访问类变量
创建完了旺财,上面不是说了为即将创建的所有狗对象绑定name和age两个属性,那我想查看我创建的旺财的属性(name,age)该怎么办呢?往下看
print(wc.name) print(wc.age) 输出结果: 旺财 1
有人会说上面我们创建的Dog类赋予每条创建的狗坐下( set() )的能力,那么我家的旺财应该也有这种能力,那么就用以下方法使旺财展示这种能力
#使旺财坐下 wc.sit() 输出结果: 小狗已经坐下了
类与对象是整个面向对象中最基础的组成单元。
类:是抽象的概念集合,表示的是一个共性的产物,类之中定义的是属性和行为(方法);
对象:对象是一种个性的表示,表示一个独立的个体,每个对象拥有自己独立的属性,依靠属性来区分不同对象。
类是对象的模板,对象是类的实例。类只有通过对象才可以使用,而在开发之中应该先产生类,之后再产生对象。类不能直接使用,对象是可以直接使用的,用通俗的话来说在python中,用变量表示特征,用函数表示技能,因而具有相同特征和技能的一类事物就是‘类’,对象是则是这一类事物中具体的一个(比如上面我创建的旺财是实例化了一条独一无二的狗)。
上面我是这样创建一个Dog类的
class Dog:#创建一个狗类 role = 'dog' # 狗的角色属性都是狗 # __init__是一个特殊方法用于在创建对象时进行初始化操作 # 通过这个方法我们可以为狗对象绑定name和age两个属性 def __init__(self, name, age): self.name = name self.age = age def sit(self): '''模仿小狗接到命令(对象调用该函数)时坐下''' print('小狗已经坐下了')
我们可以发现类里面有这样一个__init__函数,每次创建类的实例对象时, __init__函数就会自动被调用,无论它里面有什么样的变量、计算,统统会自动调用。
理解__init__函数需要搞清楚以下三点:
- 1、带有两个下划线开头的函数是声明该属性为私有,不能在类地外部被使用或直接访问
- 2、init函数(方法)支持带参数的类的初始化 ,也可为声明该类的属性
- 3、init函数(方法)的第一个参数必须是 self(self为习惯用法,也可以用别的名字),后续参数则可 以自由指定,和定义函数没有任何区别。
至于它的参数必须叫做self吗?
在实例化时自动将对象/实例本身传给__init__的第一个参数,你也可以给他起个别的名字,但是正常人都不会这么做。
- 1.名称固定,必须为__init__()
- 2.第一个参数固定,必须为self。self指的就是刚刚创建好的示例对象。
- 3.构造函数通常用来初始化示例属性,如下代码就是初始化示例属性:
- 4.通过类名(参数列表),来调用构造函数,调用后,将创建好的对象返回给相应的变量。
- 5.init()方法:初始化创建好的对象,初始化指的是:"给实例属性赋值"
- 6.new()方法:用于创建对象,但我们一般无需定义该方法。
一:我们定义的类的属性到底存到哪里了?有两种方式查看 dir(类名):查出的是一个名字列表 类名.__dict__:查出的是一个字典,key为属性名,value为属性值 二:特殊的类属性 类名.__name__# 类的名字(字符串) 类名.__doc__# 类的文档字符串 类名.__base__# 类的第一个父类(在讲继承时会讲) 类名.__bases__# 类所有父类构成的元组(在讲继承时会讲) 类名.__dict__# 类的字典属性 类名.__module__# 类定义所在的模块 类名.__class__# 实例对应的类(仅新式类中)
类中没有__slots__属性时,可以进行动态添加属性
class Dog(object): def __init__(self, name, age): self.name = name self.age = age p = Dog('lucy', 18) p.city = '上海' print(p.city) # 上海
当类中有__slots__时,可以对属性进行控制。只可以设置__slots__中的属性,如果设置的属性不在范围内,会报错。
class Dog(object): __slots__ = ('name', 'age', 'sex') def __init__(self, name, age): self.name = name self.age = age p = Dog('lucy', 18) p.sex = '男' p.city = '上海' # 报错 print(p.sex) # 男
类的文档信息
class Person(object): """ 这是人的文档信息 """ __slots__ = ('name', 'age', 'sex') def __init__(self, name, age): self.name = name self.age = age p = Person('lucy', 18) print(p.__doc__) # 对象名.__doc__ print(Person.__doc__) # 类名.__doc__ 输出结果: 这是人的文档信息 这是人的文档信息
获取对象所在的模块
test.py中的代码如下:class Person(object): __slots__ = ('name', 'age', 'sex') def __init__(self, name, age): self.name = name self.age = age demo.py中的代码: import test p = test.Person('lucy', 18) print(p.__module__) # test
获取对象的类名(全称__main__.类名)class Person(object): __slots__ = ('name', 'age', 'sex') def __init__(self, name, age): self.name = name self.age = age p = Person('lucy', 18) print(p.__class__) #<class '__main__.Person'> 输出结果: <class '__main__.Person'>
以字典的形式,显示对象所有的属性
class Person(object): def __init__(self, name, age): self.name = name self.age = age p = Person('lucy', 18) print(p.__dict__) # {'name': 'lucy', 'age': 18} 输出结果: {'name': 'lucy', 'age': 18}
与dir(对象名)等价。查看一个对象支持的所有属性和方法
class Person(object): __slots__ = ('name', 'age') def __init__(self, name, age): self.name = name self.age = age p = Person('lucy', 18) print(p.__dir__()) print(dir(p)) print(set(p.__dir__()) - set(dir(p)))
输出结果
不能直接把一个对象当做字典来使用
class Person(object): def __init__(self, name, age, city): self.name = name self.age = age self.city = city p = Person('张三', 18, '襄阳') # 我想把age修改成20,会报错 p['age'] = 20 # [] 语法会调用对象的 __setitem__方法
利用__setitem__方法,可以进行修改和添加属性
class Person(object): def __init__(self, name, age, city): self.name = name self.age = age self.city = city def __setitem__(self, key, value): self.__dict__[key] = value p = Person('张三', 18, '襄阳') p['age'] = 20 p['sex'] = '男' print(p.__dict__) # {'name': '张三', 'age': 20, 'city': '襄阳', 'sex': '男'} print(p.age) 输出结果: {'name': '张三', 'age': 20, 'city': '襄阳', 'sex': '男'} 20
getitem__的作用是什么呢?说白了就是类中一个特殊方法,类对象可以像字典对象那样根据key取值(dict['key']),如类对象Object['key'],系统会自动调用__getitem__方法(所以你在看pytorch里面Dataloader的时候没有发现主函数直接调用__getitem,原来是系统自动调用),然后返回该方法定义return值,说到这里,有一点需要强调哦, 字典取值时key不存在,会抛出异常,而类对象的key是否存在,都会调用__getitem__方法并返回其规定的值,下面进入实例。
class Person(object): def __init__(self, name, age, city): self.name = name self.age = age self.city = city def __getitem__(self, item): return self.__dict__[item] p = Person('张三', 18, '襄阳') print(p['name']) # 张三 输出结果: 张三
delitem(self,key):
这个方法在对对象的组成部分使用__del__语句的时候被调用,应删除与key相关联的值。同样,仅当对象可变的时候,才需要实现这个方法。
class Person(object): def __init__(self, name, age, city): self.name = name self.age = age self.city = city def __delitem__(self, key): print(f'删除的属性{key}') p = Person('张三', 18, '襄阳') del p['city'] # 删除的属性city 输出结果: 删除的属性city
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev
我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss
好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象
我在Rails工作并有以下类(class):classPlayer当我运行时bundleexecrailsconsole然后尝试:a=Player.new("me",5.0,"UCLA")我回来了:=>#我不知道为什么Player对象不会在这里初始化。关于可能导致此问题的操作/解释的任何建议?谢谢,马里奥格 最佳答案 havenoideawhythePlayerobjectwouldn'tbeinitializedhere它没有初始化很简单,因为你还没有初始化它!您已经覆盖了ActiveRecord::Base初始化方法,但您没有调
我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser
我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的rubyyaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir
假设我有一个FireNinja我的数据库中的对象,使用单表继承存储。后来才知道他真的是WaterNinja.将他更改为不同的子类的最干净的方法是什么?更好的是,我很想创建一个新的WaterNinja对象并替换旧的FireNinja在数据库中,保留ID。编辑我知道如何创建新的WaterNinja来self现有FireNinja的对象,我也知道我可以删除旧的并保存新的。我想做的是改变现有项目的类别。我是通过创建一个新对象并执行一些ActiveRecord魔法来替换行,还是通过对对象本身做一些疯狂的事情,或者甚至通过删除它并使用相同的ID重新插入来做到这一点,这是问题的一部分。