首页 文章 精选 留言 我的

精选列表

搜索[快速入门],共10000篇文章
优秀的个人博客,低调大师

一文了解各大图数据库查询语言(Gremlin vs Cypher vs nGQL)| 操作入门

文章的开头我们先来看下什么是图数据库,根据维基百科的定义:图数据库是使用图结构进行语义查询的数据库,它使用节点、边和属性来表示和存储数据。 虽然和关系型数据库存储的结构不同(关系型数据库为表结构,图数据库为图结构),但不计各自的性能问题,关系型数据库可以通过递归查询或者组合其他 SQL 语句(Join)完成图查询语言查询节点关系操作。得益于 1987 年 SQL 成为国际标准化组织(ISO)标准,关系型数据库行业得到了很好的发展。同 60、70 年代的关系型数据库类似,图数据库这个领域的查询语言目前也没有统一标准,虽然 19 年 9 月经过国际 SQL 标准委员会投票表决,决定将图查询语言(Graph Query Language)纳为一种新的数据库查询语言,但 GQL 的制定仍需要一段时间。 介于市面上没有统一的图查询语言标准,在本文中我们选取市面上主流的几款图查询语言来分析一波用法,由于篇幅原因本文旨在简单介绍图查询语言和常规用法,更详细的内容将在进阶篇中讲述。 图查询语言·介绍 图查询语言 Gremlin Gremlin是 Apache ThinkerPop 框架下的图遍历语言。Gremlin 可以是声明性的也可以是命令性的。虽然 Gremlin 是基于 Groovy 的,但具有许多语言变体,允许开发人员以 Java、JavaScript、Python、Scala、Clojure 和 Groovy 等许多现代编程语言原生编写 Gremlin 查询。 支持图数据库:Janus Graph、InfiniteGraph、Cosmos DB、DataStax Enterprise(5.0+) 、Amazon Neptune 图查询语言 Cypher Cypher 是一个描述性的图形查询语言,允许不必编写图形结构的遍历代码对图形存储有表现力和效率的查询,和 SQL 很相似,Cypher 语言的关键字不区分大小写,但是属性值,标签,关系类型和变量是区分大小写的。 支持图数据库: Neo4j、RedisGraph、AgensGraph 图查询语言 nGQL nGQL 是一种类 SQL 的声明型的文本查询语言,nGQL 同样是关键词大小写不敏感的查询语言,目前支持模式匹配、聚合运算、图计算,可无嵌入组合语句。 支持图数据库:Nebula Graph 图查询语言·术语篇 在比较这 3 个图查询语言之前,我们先来看看他们各自的术语,如果你翻阅他们的文档会经常见到下面这些“关键字”,在这里我们不讲用法,只看这些图数据库常用概念在这 3 个图数据库文档中的叫法。 术语 Gremlin Cypher nGQL 点 Vertex Node Vertex 边 Edge Relationship Edge 点类型 Label Label Tag 边类型 label RelationshipType edge type 点 ID vid id(n) vid 边 ID eid id(r) 无 插入 add create insert 删除 drop delete delete / drop 更新属性 setProperty set update 我们可以看到大体上对点和边的叫法类似,只不过 Cypher 中直接使用了 Relationship 关系一词代表边。其他的术语基本都非常直观。 图查询语言·语法篇 了解过 Gremlin、Cypher、nGQL 中常见的术语之后,我们来看看使用这 3 个图查询语言过程中会需要了解的常规语法。 图 # Gremlin 创建图 g = TinkerGraph.open().traversal() # nGQL 创建图空间 CREATE SPACE gods 点 图结构由点和边组成,一条边连接两个点。在 Gremlin 和 nGQL 中称之为 Vertex,Cypher 则称之为 Node。如何在图数据库中新建一个点呢?可以参考下面的语法 # Gremlin 创建/插入点 g.addV(vertexLabel).property() # Cypher 创建点 CREATE (:nodeLabel {property}) # nGQL 创建/插入点 INSERT VERTEX tagName (propNameList) VALUES vid:(tagKey propValue) 点类型 点允许有对应的类型,在 Gremlin 和 Cypher 叫 label,在 nGQL 中为 tag。点类型可对应有多种属性(Property),例如 _Person _可以有 name、_age _等属性。 创建点类型 点类型相关的语法示例如下: # Gremlin 创建点类型 g.addV(vertexLabel).property() # nGQL 创建点类型 CREATE tagName(PropNameList) 这里说明下,无论在 Gremlin 和 nGQL 中存在类似IF NOT EXISTS 用法,即:如果不存在则创建,存在则直接返回。 查看点类型 创建好点之后如何查看点类型呢,可以参考以下方式。 # Gremlin 查看(获取)点类型 g.V().label().dedup(); # Cypher 查看点类型方法 1 MATCH (n) RETURN DISTINCT labels(n) # Cypher 查看点类型方法 2 CALL db.labels(); # nGQL 查看点类型 SHOW TAGS 点的 CRUD 上面简单介绍了点、点类型,下面进入数据库基本 DML——CRUD,在上文介绍点时顺便介绍了点的创建和插入,这里说下如何插入特定类型的点,和点的获取、删除和更新。 插入特定类型点 和插入点的操作类似,只不过需要指定某种点类型。语法参考: # Gremlin 插入特定类型点 g.addV(String vertexLabel).property() # Cypher 插入特定类型点 CREATE (node:label) # nGQL 插入特定类型点 INSERT VERTEX <tag_name> (prop_name_list) VALUES <vid>:(prop_value_list) 查看点 # Gremlin 查看点 g.V(<vid>) # Cypher 查看点 MATCH (n) WHERE condition RETURN properties(n) # nGQL 查看点 FETCH PROP ON <tag_name> <vid> 删除点 术语篇中提过 nGQL 中删除操作对应单词有Delete和Drop,在 nGQL 中 Delete 一般用于点边,Drop 用于 Schema 删除,这点和 SQL 的设计思路是一样的。 # Gremlin 删除点 g.V(<vid>).drop() # Cypher 删除点 MATCH (node:label) DETACH DELETE node # nGQL 删除点 DELETE VERTEX <vid> 更新点 用数据库的小伙伴都知道数据的常态是数据变更,来瞅瞅这 3 个图查询是使用什么语法来更新点数据的吧 # Gremlin 更新点 g.V(<vid>).property() # Cypher 更新点 SET n.prop = V # nGQL 更新点 UPDATE VERTEX <vid> SET <update_columns> 可以看到 Cypher 和 nGQL 都使用 SET 关键词来设置点对应的类型值,只不过 nGQL 中多了 UPDATE 关键词来标识操作,Gremlin 的操作和上文提到的查看点类似,只不过增加了变更 property 值操作。 边 在 Gremlin 和 nGQL 称呼边为 Edge,而 Cypher 称之为 Relationship。下面进入到边相关的语法内容 边类型 和点一样,边也可以有对应的类型 # Gremlin 创建边类型 g.edgeLabel() # nGQL 创建边类型 CREATE EDGE edgeTypeName(propNameList) 边的 CRUD 说完边类型应该进入到边的常规操作部分了 插入指定边类型的边 可以看到和点的使用语法类似,只不过在 Cypher 和 nGQL 中分别使用 -[]->和 ->来表示关系,而 Gremlin 则用 to()关键词来标识指向关系,在使用这 3 种图查询语言的图数据库中的边均为有向边,下图左边为有向边,右边为无向边。 # Gremlin 插入指定边类型的边 g.addE(String edgeLabel).from(v1).to(v2).property() # Cypher 插入指定边类型的边 CREATE (<node1-name>:<label1-name>)- [(<relationship-name>:<relationship-label-name>)] ->(<node2-name>:<label2-name>) # nGQL 插入指定边类型的边 INSERT EDGE <edge_name> (<prop_name_list>) VALUES <src_vid> -> <dst_vid>: \ (<prop_value_list>) 删除边 # Gremlin 删除边 g.E(<eid>).drop() # Cypher 删除边 MATCH (<node1-name>:<label1-name>)-[r:relationship-label-name]->() DELETE r # nGQL 删除边 DELETE EDGE <edge_type> <src_vid> -> <dst_vid> 查看指定边 # Gremlin 查看指定边 g.E(<eid>) # Cypher 查看指定边 MATCH (n)-[r:label]->() WHERE condition RETURN properties(r) # nGQL 查看指定边 FETCH PROP ON <edge_name> <src_vid> -> <dst_vid> 其他操作 除了常规的点、边 CRUD 外,我们可以简单看看这 3 种图查询语言的组合查询。 指定点查指定边 # Gremlin 指定点查指定边 g.V(<vid>).outE(<edge>) # Cypher 指定点查指定边 Match (n)->[r:label]->[] WHERE id(n) = vid RETURN r # nGQL 指定点查指定边 GO FROM <vid> OVER <edge> 沿指定点反向查询指定边 在反向查询中,Gremlin 使用了 in 来表示反向关系,而 Cypher 则更直观的将指向箭头反向变成 <-来表示反向关系,nGQL 则用关键词 REVERSELY来标识反向关系。 # Gremlin 沿指定点反向查询指定边 g.V(<vid>).inE(<edge>) # Cypher 沿指定点反向查询指定边 MATCH (n)<-[r:label]-() # nGQL 沿指定点反向查询指定边 GO FROM <vid> OVER <edge> REVERSELY 无向遍历 如果在图中,边的方向不重要(正向、反向都可以),那 Gremlin 使用 both(),Cypher 使用 -[]-,nGQL使用关键词 BIDIRECT。 # Traverse edges with specified vertices Gremlin g.V(<vid>).bothE(<edge>) # Traverse edges with specified vertices Cypher MATCH (n)-[r:label]-() # Traverse edges with specified vertices nGQL GO FROM <vid> OVER <edge> BIDIRECT 沿指定点查询指定边 N 跳 Gremlin 和 nGQL 分别用 times 和 step 来表示 N 跳关系,而 Cypher 用 relationship*1..N来表示 N 跳关系。 # Gremlin 沿指定点查询指定边 N 跳 g.V(<vid>).repeat(out(<edge>)).times(N) # Cypher 沿指定点查询指定边 N 跳 MATCH (n)-[r:label*N]->() WHERE condition RETURN r # nGQL 沿指定点查询指定边 N 跳 GO N STEPS FROM <vid> OVER <edge> 返回指定两点路径 # Gremlin 返回指定两点路径 g.V(<vid>).repeat(out()).until(<vid>).path() # Cypher 返回指定两点路径 MATCH p =(a)-[.*]->(b) WHERE condition RETURN p # nGQL 返回指定两点路径 FIND ALL PATH FROM <vid> TO <vid> OVER * 图查询语言·实操篇 说了一通语法之后,是时候展示真正的技术了——来个具体一点的例子。 示例图:The Graphs of Gods 实操示例使用了 Janus Graph 的示例图 The Graphs of Gods。该图结构如下图所示,描述了罗马万神话中诸神关系。 插入数据 # 插入点 ## nGQL nebula> INSERT VERTEX character(name, age, type) VALUES hash("saturn"):("saturn", 10000, "titan"), hash("jupiter"):("jupiter", 5000, "god"); ## Gremlin gremlin> saturn = g.addV("character").property(T.id, 1).property('name', 'saturn').property('age', 10000).property('type', 'titan').next(); ==>v[1] gremlin> jupiter = g.addV("character").property(T.id, 2).property('name', 'jupiter').property('age', 5000).property('type', 'god').next(); ==>v[2] gremlin> prometheus = g.addV("character").property(T.id, 31).property('name', 'prometheus').property('age', 1000).property('type', 'god').next(); ==>v[31] gremlin> jesus = g.addV("character").property(T.id, 32).property('name', 'jesus').property('age', 5000).property('type', 'god').next(); ==>v[32] ## Cypher cypher> CREATE (src:character {name:"saturn", age: 10000, type:"titan"}) cypher> CREATE (dst:character {name:"jupiter", age: 5000, type:"god"}) # 插入边 ## nGQL nebula> INSERT EDGE father() VALUES hash("jupiter")->hash("saturn"):(); ## Gremlin gremlin> g.addE("father").from(jupiter).to(saturn).property(T.id, 13); ==>e[13][2-father->1] ## Cypher cypher> CREATE (src)-[rel:father]->(dst) 删除数据 # nGQL nebula> DELETE VERTEX hash("prometheus"); # Gremlin gremlin> g.V(prometheus).drop(); # Cypher cypher> MATCH (n:character {name:"prometheus"}) DETACH DELETE n 更新数据 # nGQL nebula> UPDATE VERTEX hash("jesus") SET character.type = 'titan'; # Gremlin gremlin> g.V(jesus).property('age', 6000); ==>v[32] # Cypher cypher> MATCH (n:character {name:"jesus"}) SET n.type = 'titan'; 查看数据 # nGQL nebula> FETCH PROP ON character hash("saturn"); =================================================== | character.name | character.age | character.type | =================================================== | saturn | 10000 | titan | --------------------------------------------------- # Gremlin gremlin> g.V(saturn).valueMap(); ==>[name:[saturn],type:[titan],age:[10000]] # Cypher cypher> MATCH (n:character {name:"saturn"}) RETURN properties(n) ╒════════════════════════════════════════════╕ │"properties(n)" │ ╞════════════════════════════════════════════╡ │{"name":"saturn","type":"titan","age":10000}│ └────────────────────────────────────────────┘ 查询 hercules 的父亲 # nGQL nebula> LOOKUP ON character WHERE character.name == 'hercules' | \ -> GO FROM $-.VertexID OVER father YIELD $$.character.name; ===================== | $$.character.name | ===================== | jupiter | --------------------- # Gremlin gremlin> g.V().hasLabel('character').has('name','hercules').out('father').values('name'); ==>jupiter # Cypher cypher> MATCH (src:character{name:"hercules"})-[:father]->(dst:character) RETURN dst.name ╒══════════╕ │"dst.name"│ ╞══════════╡ │"jupiter" │ └──────────┘ 查询 hercules 的祖父 # nGQL nebula> LOOKUP ON character WHERE character.name == 'hercules' | \ -> GO 2 STEPS FROM $-.VertexID OVER father YIELD $$.character.name; ===================== | $$.character.name | ===================== | saturn | --------------------- # Gremlin gremlin> g.V().hasLabel('character').has('name','hercules').out('father').out('father').values('name'); ==>saturn # Cypher cypher> MATCH (src:character{name:"hercules"})-[:father*2]->(dst:character) RETURN dst.name ╒══════════╕ │"dst.name"│ ╞══════════╡ │"saturn" │ └──────────┘ 查询年龄大于 100 的人物 # nGQL nebula> LOOKUP ON character WHERE character.age > 100 YIELD character.name, character.age; ========================================================= | VertexID | character.name | character.age | ========================================================= | 6761447489613431910 | pluto | 4000 | --------------------------------------------------------- | -5860788569139907963 | neptune | 4500 | --------------------------------------------------------- | 4863977009196259577 | jupiter | 5000 | --------------------------------------------------------- | -4316810810681305233 | saturn | 10000 | --------------------------------------------------------- # Gremlin gremlin> g.V().hasLabel('character').has('age',gt(100)).values('name'); ==>saturn ==>jupiter ==>neptune ==>pluto # Cypher cypher> MATCH (src:character) WHERE src.age > 100 RETURN src.name ╒═══════════╕ │"src.name" │ ╞═══════════╡ │ "saturn" │ ├───────────┤ │ "jupiter" │ ├───────────┤ │ "neptune" │ │───────────│ │ "pluto" │ └───────────┘ 从一起居住的人物中排除 pluto 本人 # nGQL nebula> GO FROM hash("pluto") OVER lives YIELD lives._dst AS place | GO FROM $-.place OVER lives REVERSELY WHERE \ $$.character.name != "pluto" YIELD $$.character.name AS cohabitants; =============== | cohabitants | =============== | cerberus | --------------- # Gremlin gremlin> g.V(pluto).out('lives').in('lives').where(is(neq(pluto))).values('name'); ==>cerberus # Cypher cypher> MATCH (src:character{name:"pluto"})-[:lives]->()<-[:lives]-(dst:character) RETURN dst.name ╒══════════╕ │"dst.name"│ ╞══════════╡ │"cerberus"│ └──────────┘ Pluto 的兄弟们 # which brother lives in which place? ## nGQL nebula> GO FROM hash("pluto") OVER brother YIELD brother._dst AS god | \ GO FROM $-.god OVER lives YIELD $^.character.name AS Brother, $$.location.name AS Habitations; ========================= | Brother | Habitations | ========================= | jupiter | sky | ------------------------- | neptune | sea | ------------------------- ## Gremlin gremlin> g.V(pluto).out('brother').as('god').out('lives').as('place').select('god','place').by('name'); ==>[god:jupiter, place:sky] ==>[god:neptune, place:sea] ## Cypher cypher> MATCH (src:Character{name:"pluto"})-[:brother]->(bro:Character)-[:lives]->(dst) RETURN bro.name, dst.name ╒═════════════════════════╕ │"bro.name" │"dst.name"│ ╞═════════════════════════╡ │ "jupiter" │ "sky" │ ├─────────────────────────┤ │ "neptune" │ "sea" │ └─────────────────────────┘ 附录 Nebula Graph:一个开源的分布式图数据库 GitHub:https://github.com/vesoft-inc/nebula 官方论坛:https://discuss.nebula-graph.io 知乎:zhihu.com/org/nebulagraph/posts 微博:weibo.com/nebulagraph

优秀的个人博客,低调大师

Python爬虫入门教程 50-100 Python3爬虫爬取VIP视频-Python爬虫6操作

爬虫背景 原计划继续写一下关于手机APP的爬虫,结果发现夜神模拟器总是卡死,比较懒,不想找原因了,哈哈,所以接着写后面的博客了,从50篇开始要写几篇python爬虫的骚操作,也就是用Python3通过爬虫实现一些小工具。 Python3 VIP视频下载器 这种软件或者网站满天都是了,就是在线观看收费网站的VIP视频,你只要会玩搜索引擎或者是一个程序员基本都知道,虽说一直在被封杀,但是能赚钱的地方就一定有人钻漏洞。今天要实现的就是通过别人的API在Python中下载ts视频到本地,自己去百度一下TS视频是什么吧。 找相关的接口 我随便搜索了一下,那是非常多的,版权问题,就不放相关的地址了,当然在代码中还是会出现一下的。 我找到这个接口应该是目前相对比较稳定的,并且还在更新的 我看了一下,他中间主要通过三个API整体实现的页面逻辑 首先你先去优酷啊,

优秀的个人博客,低调大师

java spring cloud版b2b2c社交电商--服务网关 Spring Cloud GateWay 入门

Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。 Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。 相关概念: Route(路由):这是网关的基本构建块。它由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配。 Predicate(断言):这是一个 Java 8 的 Predicate。输入类型是一个 ServerWebExchange。我们可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。 Filter(过滤器):这是org.springframework.cloud.gateway.filter.GatewayFilter的实例,我们可以使用它修改请求和响应。 工作流程: 客户端向 Spring Cloud Gateway 发出请求。如果 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。 过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。 Spring Cloud Gateway 的特征: 基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0 动态路由 Predicates 和 Filters 作用于特定路由 集成 Hystrix 断路器 集成 Spring Cloud DiscoveryClient 易于编写的 Predicates 和 Filters 限流 路径重写 Spring Cloud Gateway 与 Zuul的区别: 1、Zuul 1.x,是一个基于阻塞 I/ O 的 API Gateway 2、Zuul 1.x 基于Servlet 2. 5,使用阻塞架构,它不支持任何长连接,如 WebSocket。 Zuul 的设计模式和Nginx较像,每次 I/ O 操作都是从工作线程中选择一个执行,请求线程被阻塞到工作线程完成,但是差别是Nginx 用C++ 实现,Zuul 用 Java 实现,而 JVM 本身会有第一次加载较慢的情况,使得Zuul 的性能相对较差。 3、Zuul 2.x,基于 Netty 非阻塞、支持长连接,但 Spring Cloud 目前还没有整合。 Zuul 2.x的性能较 Zuul 1.x 有较大提升。在性能方面,根据官方提供的基准测试, Spring Cloud Gateway 的 RPS(每秒请求数)是Zuul 的 1. 6 倍。 4、Spring Cloud Gateway 建立 在 Spring Framework 5、 Project Reactor 和 Spring Boot 2 之上, 使用非阻塞 API。 5、Spring Cloud Gateway 还 支持 WebSocket, 并且 与 Spring 紧密集成, 拥有更好的开发体验。

优秀的个人博客,低调大师

[雪峰磁针石博客]python 3.7极速入门教程1安装:Linux(Ubuntu 18.04)及Windows上安装Anaconda

安装 Linux安装 Anaconda是最受欢迎的python数据科学和机器学习平台,用于大规模数据处理,预测分析和科学计算。Anaconda发行版附带了1,000多个数据包,conda命令行工具和Anaconda Navigator的桌面图形用户界面。一般而言,使用Anaconda比直接使用官方的python更好用。 本教程将指导您完成在Ubuntu 18.04上下载和安装Anaconda Python Distribution。通常也适合多数其他Linux。 下载Anaconda https://www.anaconda.com/download/#linux 在撰写本文时,Anaconda的最新稳定版本是5.3版本。 图片.png 请选择64-Bit (x86) Installer 进行下载,尽量不要用python2.7,很多库已经不提供python2.7支持。 点击链接就会进行下载,同时会弹出信息收集框,直接关闭就好。 图片.png 安装Anaconda $ sudo sh Anaconda3-5.3.0-Linux-x86_64.sh [sudo] andrew 的密码: Welcome to Anaconda3 5.3.0 In order to continue the installation process, please review the license agreement. Please, press ENTER to continue >>> # 按下回车 ... # 多次按空格读完license。 Do you accept the license terms? [yes|no] [no] >>> yes # 输入yes - Press ENTER to confirm the location - Press CTRL-C to abort the installation - Or specify a different location below ... [/home/andrew/anaconda3] >>> /usr/local/anaconda3 # 输入自定义目录 ... # 安装根据机器性能,通常需要持续几分钟。 # 讨论 钉钉群21745728 qq群144081101 567351477 # 本文地址:https://www.jianshu.com/p/9fe9ff999234 Do you wish to proceed with the installation of Microsoft VSCode? [yes|no] #选择no,一般来说自带的spyder和专业IDE wingIDE更好用。 ... Do you wish the installer to initialize Anaconda3 in your /home/andrew/.bashrc ? [yes|no] [no] >>> # 选择yes Initializing Anaconda3 in /home/andrew/.bashrc A backup will be made to: /home/andrew/.bashrc-anaconda3.bak For this change to become active, you have to open a new terminal. Thank you for installing Anaconda3! =========================================================================== Anaconda is partnered with Microsoft! Microsoft VSCode is a streamlined code editor with support for development operations like debugging, task running and version control. To install Visual Studio Code, you will need: - Administrator Privileges - Internet connectivity Visual Studio Code License: https://code.visualstudio.com/license Do you wish to proceed with the installation of Microsoft VSCode? [yes|no] >>> no 添加“export PATH=/usr/local/anconda3/bin:$PATH“ 到/etc/profile,这样所有用户都可以使用Anaconda python3.7了。 上面的.bashrc在刚才安装时添加了如下内容: # added by Anaconda3 5.3.0 installer # >>> conda init >>> # !! Contents within this block are managed by 'conda init' !! __conda_setup="$(CONDA_REPORT_ERRORS=false '/usr/local/anaconda3/bin/conda' shell.bash hook 2> /dev/null)" if [ $? -eq 0 ]; then \eval "$__conda_setup" else if [ -f "/usr/local/anaconda3/etc/profile.d/conda.sh" ]; then . "/usr/local/anaconda3/etc/profile.d/conda.sh" CONDA_CHANGEPS1=false conda activate base else \export PATH="/usr/local/anaconda3/bin:$PATH" fi fi unset __conda_setup~/.bashrc # <<< conda init <<< 验证安装 通过重新登陆或者执行'source ~/.bashrc"加载环境变量。 $ conda info active environment : base active env location : /usr/local/anaconda3 shell level : 1 user config file : /home/andrew/.condarc populated config files : conda version : 4.5.11 conda-build version : 3.15.1 python version : 3.7.0.final.0 base environment : /usr/local/anaconda3 (read only) channel URLs : https://repo.anaconda.com/pkgs/main/linux-64 https://repo.anaconda.com/pkgs/main/noarch https://repo.anaconda.com/pkgs/free/linux-64 https://repo.anaconda.com/pkgs/free/noarch https://repo.anaconda.com/pkgs/r/linux-64 https://repo.anaconda.com/pkgs/r/noarch https://repo.anaconda.com/pkgs/pro/linux-64 https://repo.anaconda.com/pkgs/pro/noarch package cache : /usr/local/anaconda3/pkgs /home/andrew/.conda/pkgs envs directories : /home/andrew/.conda/envs /usr/local/anaconda3/envs platform : linux-64 user-agent : conda/4.5.11 requests/2.19.1 CPython/3.7.0 Linux/4.15.0-36-generic ubuntu/18.04 glibc/2.27 UID:GID : 1000:1000 netrc file : None offline mode : False $ python Python 3.7.0 (default, Jun 28 2018, 13:15:42) [GCC 7.2.0] :: Anaconda, Inc. on linux Type "help", "copyright", "credits" or "license" for more information. >>> 至此,已经安装完毕。 更新Anaconda 一般是有新版本发布时才需要使用。 $ conda update conda $ conda update anaconda 删除Anaconda 一般是有新版本发布时才需要使用。 $sudo rm -rf /usr/local/anaconda3 删除上面~/.bashrc和/etc/profile的修改 清空隐藏文件: rm -rf ~/.condarc ~/.conda ~/.continuum 参考资料 讨论qq群144081101 591302926 567351477 钉钉免费群21745728 本文最新版本地址 本文涉及的python测试开发库 谢谢点赞! 本文相关海量书籍下载 https://linuxize.com/post/how-to-install-anaconda-on-ubuntu-18-04/ Windows安装 下载,比如:https://repo.anaconda.com/archive/Anaconda3-5.3.0-Windows-x86_64.exe 执行: 图片.png 图片.png 图片.png 图片.png 图片.png 上面红色的部分一定要选中,这样会帮你自动配置环境变量。 图片.png 图片.png 图片.png 图片.png 新开cmd,输入python 图片.png 命令行输入spyder 图片.png wingide 图片.png MAC安装 MAC的安装和Windows类似。

优秀的个人博客,低调大师

对于初学C++的人来说,你觉得从入门到精通需要多长时间?应该怎么学?

作为已经搞了十几年代码的老司机,对于这种动不动就喊着精通某种语言的初学者有点无奈,现在很多刚毕业或者毕业没有几年的程序员喊着精通几种编程语言,而且这种基本上在面试上遇到老程序员都会遭到技术细节的洗礼,反而越是干的时间越长的程序员越是在介绍自己的时候都是写上熟练某种语言,c++作为一种几乎公认的最难的编程语言,也是一门够得上学一辈子的编程语言,现在很多编程书籍的名字起的也是让人觉得很无语,21天精通某某语言等等也是助长了浮躁之风。 为什么这么多人觉得C++难学,而且现在很多大学开设的第一门编程语言就是C++,上来就让啃最难的骨头,而且要问到多长时间能够到精通?让这些做了十几年的c++程序员情何以堪,为什么c++被认为成很难的编程语言? 1.基础语法相当繁杂,而且还在不停的更新 c++的设计理念是尽量在编译的时候通过个各种规则达到一种效果,所以很多初学者在编译的时候就能折腾很长时间,特别是泛型编程里面的各种设计模式,细节多如牛毛。由于这种设计的理念,导致很多编程大神炮轰这种理念,觉得是一门把人带沟里的编程语言,现在学校开设的c++课程主要在讲述面向对象的设计理念,基本上很少涉及到泛型编程,所以很多人觉得c++没有那么难,因为只是讲述了面向对象的理念是个什么样子,对于真正实战类型的接触的比较少,很多进入工作岗位才发现原来这东西这么难,还能这么玩,关键这还不是全部还在不停的更新,一旦踏进去想玩彻底基本很难,正因为骨头硬所以很多架构师倒是非常喜欢琢磨这门编程语言,觉得特别有味道。 2.靠近底层可以直接对底层语言,特别是指针操作 如果要找出一种编程语言既能直接操控硬件又能带有面向对象的编程思想,也只有c++了,正是因为这个特性,很多对性能要求比较大的框架都在c++的基础上进行构建,这也就是为什么很多架构师喜欢琢磨c++,根本原因还是刚需。操空底层的编程语言就会涉及到指针的操作,指针问题对于初学者来讲简直的一个噩梦,本质上不难主要计算机相关基础知识不到位的情况下,去学习理解起来就费劲很多。 很多人初学者从开源社区拿到c++写的代码源码,开始基本上看不懂,因为里面涉及到很多语法细节不说,关键还有各种编程模式,加上泛型编程里面语法,要真要能看明白代码如何实现基本上对于c++已经有了大概的认识,距离自己真实写代码已经非常接近了。 难于不难,主要是客观上评价,关键看主观上如何去对待,真正进入状态学习了,也不会去想精通不精通这码事情了,做编程有一种感觉叫水到渠成,开始觉得自己写不了代码到了一定时间自然就会写了,都是坚持学习的结果。 原文发布时间为:2018-09-28 本文作者:东辉在线 本文来自云栖社区合作伙伴“程序员互动联盟”,了解相关信息可以关注“程序员互动联盟”。

优秀的个人博客,低调大师

史上最详细的入门教程!

Python的三大神器:装饰器.迭代器与生成器!这就是Python的三大神器,好了废话不多说。直接来上干货吧! 生成器 仅仅拥有生成某种东西的能力,如果不用__next__方法是获取不到值得。 创建一个生成器函数 >>> def scq():... print("11")# 当函数代码块中遇到yield关键字的时候,这个函数就是一个生成器函数... yield 1... print("22")... yield 2... print("33")... yield 3... 把生成器赋值给一个对象 >>> r = scq() 查看r的苏剧类型并且输出r的值 >>> print(type(r),r)<class 'generator'> <generator object scq at 0x000001F117D8DF10> 当执行生成器的__next__的时候,代码会按照顺序去执行,当执行到yield时会返回并提出,yield后面的值就是返回值,然后记录代码执行的位置,并退出 执行结果 C:Python35python.exe F:/Python_code/sublime/Week5/Day03/s1.py0 1 2 3 4Process finished with exit code 0 迭代器 具有访问生成器的能力,可以访问到生成器的值,类似于生成器的__next__方法,一个一个值一个值得去迭代,只能够按照顺序的去查找。 特点: 访问者不需要关心迭代器内部的结构,仅需通过next()方法不断去取下一个内容 不能随机访问集合中的某个值 ,只能从头到尾依次访问 访问到一半时不能往回退 便于循环比较大的数据集合,节省内存 优化上面range或xrange的生成器 def irange(start, stop, step=1): while start != stop: yield start start += step else: raise StopIteration for n in irange(1, 10): """for循环只要遇到StopIteration就会停止""" print(n)ret = irange(1, 20) print(ret) # 返回一个生成器,相当于只在内存中创建了一个值 print(list(ret)) # 如果想要得到全部的值,变成列表就可以 /Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/ansheng/MyPythonCode/hello.py 1 2 3 4 5 6 7 8 9 <generator object irange at 0x1021df7d8> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] Process finished with exit code 0 想学习可以加Python学习(q-u-n )二二七,四三五,四五零 即可获取,内附:开发工具和安装包,以及视频资料系统学习路线图 Python之装饰器 现要在执行func这个函数前后执行一些操作,就可以创建一个装饰器来实现: #!/usr/bin/env python # _*_ coding: utf-8 _*_ def decorator(func): # 创建一个装饰器函数,接受的参数arg参数就是func函数名 def inner(*args, **kwargs): print("执行函数之前") ret = func(*args, **kwargs) print("执行函数之后") return ret return inner @decorator # 如果要让某个函数使用装饰器,只需要在这个函数上面加上@+装饰器名 def func(arg): print(arg) func("Hello World!") 输出结果为: /usr/bin/python3.5 /home/ansheng/Documents/PycharmProjects/blogcodes/装饰器.py 执行函数之前 Hello World! 执行函数之后 Process finished with exit code 0 多个装饰器装饰同一个函数 #!/usr/bin/env python # _*_ coding: utf-8 _*_ def decorator1(func): def inner(): print("开始之前执行装饰器01") ret = func() print("结束之后执行装饰器01") return ret return inner def decorator2(func): def inner(): print("decorator2>>>Start...") ret = func() print("decorator2>>>End...") return ret return inner @decorator1 @decorator2 def index(): print("执行函数...") index() 输出结果: /usr/bin/python3.5 /home/ansheng/Documents/PycharmProjects/blogcodes/装饰器.py 开始之前执行装饰器01 decorator2>>>Start... 执行函数... decorator2>>>End... 结束之后执行装饰器01 Process finished with exit code 0 更多实例 #!/usr/bin/env python # _*_ coding:utf-8 _*_ # Created by 安生 on 2017/2/9 """ 函数装饰器 """ def decorator(func): def wrapped(*args, **kwargs): return func(*args, **kwargs) return wrapped @decorator def func(a, b): return a + b print(func(1, 2)) """ 类装饰器 """ class decorator: def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): return self.func(*args, **kwargs) @decorator def func(a, b): return a + b print(func(1, 2)) """ 带参数的函数装饰器 """ def parameter(a, b): print(a, b) def decorator(func): def wrapped(*args, **kwargs): return func(*args, **kwargs) return wrapped return decorator @parameter(1, 2) def func(a, b): return a + b print(func(10, 20)) """ 带参数的类装饰器 """ def parameter(a, b): print(a + b) class decorator: def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): return self.func(*args, **kwargs) return decorator @parameter(1, 2) def func(a, b): return a + b print(func(10, 20)) """ 带参数的类装饰器 """ def parameter(a, b): print(a, b) def decorator(cls): class wrapped: def __init__(self, *args, **kwargs): self.cls = cls(*args, **kwargs) def __getattr__(self, item): return getattr(self.cls, item) return wrapped return decorator @parameter(1, 2) class CLS: def __init__(self): self.a = 'a' def P(self, v): print(v) obj = CLS() print(obj.a) obj.P('Hello,') """ 为函数中和类中的方法添加装饰器 """ def Call(aClass): calls = 0 def onCall(*args, **kwargs): nonlocal calls calls += 1 print('call %s to %s' % (calls, func.__name__)) return aClass(*args, **kwargs) return onCall @Call def func(a, b): return a + b print(func(1, 2)) class CLS: def __init__(self): self.a = 'a' @Call def b(self): return self.a obj = CLS() print(obj.b())

优秀的个人博客,低调大师

云栖大讲堂Java基础入门(二)—— 阿里专家与你分享:你必须注意的Java编程细节

摘要:本文主要关注如何在Java中操作一系列对象,介绍了Java的内建类型——数组,并介绍了一些操作数组的方法;随后,介绍了JDK中的集合类,一元对象的存储使用了Collection,详细介绍了Collection的分类;同时,本文展示了Map的多种实现策略;本文的重点内容是处理细节注意事项,来源于Java开发手册。 数十款阿里云产品限时折扣中,赶紧点击这里,领劵开始云上实践吧! 演讲嘉宾简介: 邢凯航(花名:弗止),Java高级开发工程师,香港大学计算机科学硕士,16年加入阿里巴巴,目前就职于研发效能事业部用户声音及代码智能化团队,负责代码中心后端开发。 本次直播视频精彩回顾,戳这里! PPT地址:https://yq.aliyun.com/download/2656 以下内容根据演讲嘉宾视频分享以及PPT整理而成。 本次的分享主要围绕以下四个方面: 一、数组 二、Collection 三、Map 四、处理细节注意事项 一、数组 数组作为java的内建类型,它的大小和类型是固定的,访问性能高效。数组的大小和类型一旦被指定,在运行期间就不能再修改;同时,Java中的数组支持边界值检查,访问或设置越界的数组都会抛出异常;另外,数组对外提供了length变量,但它只能反映最大容量,但不能反映使用的大小,可以通过将数组的使用量存储在变量中或者遍历数组,确定数组的使用量;Java JDK中Arrays工具类提供了多种操作方式,比如填充数组(fill),二分查找(binarySearch,前提是整个数组是有序的),比较数组相等(equals),数组排序(sort)等方法。因为JDK提供的容器工具类提供了更丰富的能力,因此,在日常开发中,我们使用更多的其实是容器类,只有当我们确定这些容器工具类的效率成为问题时,我们才会切换到使用数组去实现。 二、Collection 下面我们看一下这些容器工具类,首先是一元对象存储Collection。 List: List表示一系列有序的对象,List接口有多种实现,如下图所示。首先按照线程是否安全可以分为两类,线程安全指的是多个线程存取数据时都是安全的。非线程安全中最常用的是ArrayList,ArrayList基于数组实现,它跟数组类似,随机访问的操作时间复杂度是常数级别;与数组不同的是,arraylist的容量是可以自动增长的,最大容量可达到21亿。LinkedList是基于链表实现的,非线程安全,与ArrayList相比,LinkedList优势体现在,当我们经常在链表中增加删除元素时,LinkedList只需要修改前后对象的指针,而ArrayList需要拷贝剩下一部分的数组内容,因此LinkedList在这种情况下具有更高的效率。在下图中,线程安全的实现类包括Vector和CopyOnWriteArrayList,CopyOnWriteArrayList的整个add操作都是在锁的保护下进行的,修改数据时,先对数据加锁,从原有的数组中拷贝一份出来,在新的数组做写操作,写完之后,替换掉原来的数组,CopyOnWriteArrayList通过这种方式达到线程安全的目的。同时,数组定义时使用了transient关键字,达到了修改后所有线程都是可见的目的。CopyOnWriteArrayList在读取数据时,通过直接访问数组生成迭代器的快照,以此实现读取操作不需要加锁,并且可以并行访问的目的。Vector类已经不推荐使用了,Vector 的所有方法加上了 synchronized 关键字,达到线程安全的目的,与CopyOnWriteArrayList相比效率较低,在用户不考虑线程安全时,采用ArrayList或LinkedList实现。 Set: 当我们需要找到链表中一个确定的对象时,对于List类,我们需要遍历整个链表,效率比较低,因此JDK为我们提供了Set类型,表示一个集合的概念,与数学中集合的概念是类似的,Set集合中存储中不重复的元素。下图中的EnumSet仅仅用于处理枚举类型,使用位向量实现,根据枚举值的多少提供两种实现方式,这两种实现方式都是包私有的,我们需要通过EnumSet抽象类进行操作。Set类型也分为线程安全和非线程安全,另外,根据内置的集合是否有序,也可以分为两种实现。如果需要使用有序的集合,集合中的元素需要实现Comparable接口,或者让容器在构造时加入比较器对象。在有序的集合中,TreeSet是非线程安全的,内部使用TreeMap 实现,ConcurrentSkipListSet是一个有序且线程安全的集合,是基于ConcurrentSkipListMap实现的,使用ConcurrentSkipListSet进行新增,删除,查询,操作的时间复杂度能达到O(logn)。如果不需要整体有序的集合,我们可以选择别的实现,例如HashSet,它是一个非线程安全且无序的集合,基于HashMap实现。HashSet派生出的一个实现,LinkedHashSet,其保证在迭代获取时,元素获取的顺序与插入的顺序是一致的。LinkedHashSet在迭代访问Set中全部元素时,其性能优于HashSet,而在插入元素时,性能逊色于HashSet。CopyOnWriteArraySet是线程安全的,基于CopyOnWriteArrayList实现,在添加元素前,检查当前数据结构中是否包含了相同的元素,如果已经包含,就不再重复增加。 Queue: Queue代表队列,一般用于存取需要处理的任务,提供了在一端进行存取的能力。BlockingQueue接口的实现类额外提供了在存取元素时,阻塞的能力。BlockingQueue提供了多套方法,在获取元素时,如果队列为空,或者在插入元素时,队列已满,BlockingQueue将会抛出异常,或者返回一个特定值让程序去处理,或者让调用者线程无限的等待,或者在有限的时间范围内等待。Deque接口的实现类提供在两端进行存取的能力。PriorityQueue和PriorityBlockingQueue提供优先队列的能力,前者是非线程安全,后者是线程安全的。它们存取的对象需要实现Comparable接口或者提供比较器,否者会抛出异常。DelayQueue是一个延时队列,当队列中的元素达到延时的时间时才会被取出,队列中的元素最终会按照执行的时间在队列中进行排序。SynchronizedQueue并没有容量设置,每一个获取动作必须等待线程的插入动作达到匹配,在线程池中有所应用。TransferQueue接口的实现类的作用和SynchronizedQueue类似,功能有所增强。在典型生产者与消费者模式中,生产者可以采用阻塞或者非阻塞的方式,将对象传递给消费者,并且在队列上提供查询消费者情况的接口。 三、Map: Map是一种键值对的映射,在java中有多种Map的实现可供选择。实现SortedMap接口的类会按照key进行排序,TreeMap是SortedMap的一种实现,是非线程安全的,内部使用红黑树模型。使用TreeMap进行插入,删除,查找操作的时间复杂度为O(logn)。ConcurrentSkipListMap使用跳表的数据结构,插入,删除,查询操作的时间复杂度为O(logn)。跳表主要采用空间换时间的思想,跳表由多条链构成,是关键字升序排列的数据结构,包含多个级别,一个head引用指向最高的级别,最低(底部)的级别,包含所有的key,每一个级别都是其更低级别的子集,并且是有序的。对于Map,常用的实现为HashMap,concurrentHashMap。HashMap的实现结构是散列表(数组+链表)方式, LinkedHashMap增加了时间和空间上的开销,但是通过维护运行在所有条目上的双向链表,保证元素顺序的一致性。ConcurrentHashMap是线程安全的,提供了分段锁的设计,在同一个分段内,才存在竞态的关系,而不同的分段锁之间,由于不存在竞争,可以并行。相对于在整个Map上加锁的设计,ConcurrentHashMap采用分段锁,大大提高了在高并发环境下处理的能力。HashTable就是在整个Map上加锁,达到线程安全的目的。EnumMap是专门用于枚举类型的映射,提供比较高的效率。WeakHashMap和HashMap比较类似,区别在于WeakHashMap 内部是通过弱引用来管理entry的,虽然弱引用可以用来访问对象,但进行垃圾回收时弱引用并不会被考虑在内,仅有弱引用指向的对象仍然会被GC回收。在IdentityHashMap中,当且仅当两个key严格相等(key1==key2)时,IdentityHashMap才认为两个key相等;相对于普通HashMap而言,只要key1和key2通过equals()方法返回true,且它们的hashCode值相等即可。 四、处理细节注意事项 1.只要重写equals,就必须重写hashcode,如下图所示。 首先equals与hashcode间的关系是这样的:如果两个对象相同(即用equals比较返回true),那么它们的hashCode值一定要相同;如果两个对象的hashCode相同,它们并不一定相同(即用equals比较返回false)。重写hashcode方法的原因在于,为了保证同一个对象,保证在equals相同的情况下hashcode值必定相同,如果重写了equals而未重写hashcode方法,可能就会出现两个没有关系的对象equals相同的(因为equal都是根据对象的特征进行重写的),但hashcode确实不相同的。 2. 使用Map的方法keySet()/values()/entrySet()返回集合对象时,不可以对其进行添加元素操作,否则会抛出UnsupportedOperationException异常。如下图所示。 上图中的返回方法的对象都是内部的实现类,例如EntrySet,Values,因为这些都是实现类,这些类只实现了抽象集合中的部分方法,并没有重写add方法,如果在调用add方法时,实际会调用到AbstractCollection中的add方法,而抽象类中的add方法会抛出UnsupportedOperationException异常。 3. Collections类返回的对象,如:emptyList()/singletonList()等都是immutable list,不可对其进行添加或者删除元素的操作。 与前一条注意事项一样,下图展示了截取的EmptyList类重写的一些方法列表,我们发现并没有重写add方法,所以最终调用的还是AbstractCollection中的add方法,而抽象类中的add方法会抛出UnsupportedOperationException异常。 4. ArrayList的subList结果不不可强转成ArrayList,否则会抛出ClassCastException异常。 下图截取了ArrayList中的SubList,SubList是实际返回的一个类,SubList与ArrayList并没有任何的子类关系,如果强转,java会抛出ClassCastException异常。 5. 在subList场景中,高度注意对父集合元素个数的修改,会导致子列表的遍历、增加、删除均会产生ConcurrentModificationException 异常。 下图展示了ArrayList中的subList的一个Iterator,它的next()方法中,在执行的时候会先调用checkModification()方法,这个方法中会对expectedModcount进行判断,任何父集合元素的修改都会引起这个值的变化,在子集合遍历的时候抛出ConcurrentModificationException 异常。 6. 使用集合转数组的方法,必须使用集合的toArray(T[] array),如下图所示,传入的是类型完全一样的数组,大小就是list.size()。 直接使用toArray()无参数方法时存在问题,这个方法只能返回Object类型的数组,如果强制转换成其他类型的数组,将会出现ClassCastException异常。当我们使用toArray带参数方法时,入参分配的参数空间不够大的话,toArray方法内部将重新分配内存空间,并返回新数组的地址;如果数组元素的个数大于实际所需,数组的下表从list.size(),一直到数组末尾全部元素都会被置为null,其他数组元素保持不变。因此,我们最好将方法入参数组的大小定义成集合元素的个数。 7. 使用工具类Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的add/remove/clear方法会抛出UnsupportedOperationException异常 。Arrays.asList()方法内部会用到ArrayList实现类,这个不同于java.util下的ArrayList类,从下图中可以看到,这个类并没有重写add和remove方法,而这些方法会继承自AbstractList抽象类中的add,remove,clear方法。add和remove操作会抛出UnsupportedOperationException异常。 8. 不要在foreach循环⾥进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。 ArrayList的遍历是通过下面的内部类Iterator来实现的,遍历的时候,每当Iterator获取下一个元素时,如下图所示,都会调用checkForComodification()去检查arrayList是否被修改过。如果被修改了,就会抛出一个ConcurrentModificationException异常。 本文由云栖志愿小组沈金凤整理,编辑百见

优秀的个人博客,低调大师

一脸懵逼加从入门到绝望学习hadoop之Caused by: java.net.UnknownHostException: master报错

windows下开发hadoop应用程序,hadoop部署在linux环境中, 在运行调试时可能会出现无法找到主机,类似异常信息如下: java.net.UnknownHostException: unknown host: master 解决办法如下: 在C:\WINDOWS\system32\drivers\etc\hosts文件中添加如下信息: 192.168.0.55 master 即把自己的虚拟机的ip地址和对应的虚拟机主机名添加一下,解决java.net.UnknownHostException: unknown host: master这个问题 停更......

优秀的个人博客,低调大师

《Android App开发入门:使用Android Studio 2.X开发环境》—— 2-2 Android 程序的设计流程

2-2 Android 程序的设计流程 Android程序设计是把程序代码和资源(Resource)分开设计的。“资源”包含界面的安排、字符串对象、图形对象、音乐对象等,这些对象都以文件的方式存放在项目的 res 文件夹下,再构建(Build)起来成为 .apk 文件,最后由用户下载安装到手机上使用。 Android 的资源以视觉部分最多,其他也包含音乐、字符串等资源,为解说方便,除非在特别谈到音乐、字符串等资源时,否则我们多以视觉资源为代表。 视觉设计和程序逻辑 原本 Android 程序是可以一直用 Java 写下去的,但那样往往工程浩大又十分复杂,因此 Android 把程序设计的工作分成两大部分:一部分专门负责做程序的视觉设计(也就是用户界面,User Interface,UI),另一部分负责程序代码(程序逻辑)的编写。And

优秀的个人博客,低调大师

《Android App开发入门:使用Android Studio 2.X开发环境》——2-5 开始动手编写程序

2-5 开始动手编写程序 在这一节中,本书将会带领大家制作第一个互动 Android App。这个范例执行后会显示“Hello world!”字符串,并且可在用户单击屏幕上的按钮时自动放大文字。 我们会遵循设计 Android App 的流程,先将视觉外观的部分设计好,再加入控制互动行为的程序逻辑。通过实际演练这个范例可以对 Android App 的设计有更清楚的认识。 创建项目后,会自动打开项目并以图形布局编辑器打开布局文件。 在新建的 Android项目中,默认的布局含有两个组件。 在设计布局的过程中,Android Studio 的语法检查程序 lint 会持续检查 XML 的内容,若有问题,则提出错误或警告。对于错误,用户当然要将之排除才行,否则就无法构建程序执行文件;至于警告,许多是 Android 的建议,在学习阶段可先忽

优秀的个人博客,低调大师

2025 年 AI 人才流动报告:AI 新发岗位数量快速增长、平均月薪超 6 万元

近日,脉脉发布《2025 年 AI 人才流动报告》。数据显示,2025年1-7月,AI新发岗位量同比增长超10倍,简历投递量也暴涨了11倍。“算法”相关人才持续紧缺,其中“搜索算法”人才紧缺度最高,5岗争2人。非技术岗数量同比增长7.7倍。 截至2025年7月,脉脉上有超1000家企业发布了7.2万个AI相关岗位,涵盖互联网大厂、外企、车企和AI垂类企业。热招企业和高薪企业均呈现大厂与垂类企业竞相发力的格局。 从新发AI岗位量来看,字节跳动招聘指数29.83位列首位,小红书18.32居第二,阿里巴巴12.25位列第三。小鹏汽车、自变量机器人、黑芝麻智能等垂直企业同样上榜。 不同规模企业的AI化进程显著加速,新发岗位AI渗透率明显提升。2025年1-7月,新经济行业新发岗位AI渗透率超过10%,较去年同期提升10倍以上,且随企业规模扩大而提升。 算法类岗位在AI领域招聘中占据核心地位。从热招岗位TOP20中,过半岗位与算法强相关。“大模型算法”以招聘指数94.16高居热招岗位榜首。在人才紧缺度TOP20的岗位中,“算法”岗位依然霸榜。其中,“搜索算法”成为AI领域人才稀缺度最高的岗位,人才供需比仅为0.39,相当于5个岗位争夺2个人才。 随着企业AI应用加速,非技术岗位数量也在迅速增加。2025年1-7月,AI领域新发非技术类的岗位量相比2024年同期增长7.74倍。产品、运营与设计成为非技术岗位的三大热招方向。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册